diff -Nru puppetserver-7.9.5/.clj-kondo/config.edn puppetserver-8.4.0/.clj-kondo/config.edn --- puppetserver-7.9.5/.clj-kondo/config.edn 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/.clj-kondo/config.edn 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,34 @@ +{:linters {:use {:level :error} + :unresolved-symbol {:level :warning :exclude [(clojure.test/is [thrown+? thrown+-with-msg? logged?]) + (puppetlabs.puppetserver.bootstrap-testutils/with-puppetserver-running [app]) + (puppetlabs.puppetserver.bootstrap-testutils/with-puppetserver-running-with-mock-jrubies [app]) + (puppetlabs.puppetserver.bootstrap-testutils/with-puppetserver-running-with-config [app]) + (puppetlabs.puppetserver.bootstrap-testutils/with-puppetserver-running-with-mock-jruby-puppet-fn [app]) + (puppetlabs.puppetserver.bootstrap-testutils/with-puppetserver-running-with-services [app]) + (puppetlabs.puppetserver.bootstrap-testutils/with-puppetserver-running-with-services-and-mock-jruby-puppet-fn [app]) + (puppetlabs.puppetserver.ruby.http-client-test/with-scripting-container [sc]) + (puppetlabs.services.jruby.jruby-metrics-service-test/with-metrics-test-env) + (puppetlabs.services.jruby.jruby-puppet-service/with-jruby-puppet [jruby-puppet]) + (puppetlabs.services.jruby-pool-manager.jruby-core/with-jruby-instance [jruby-instance]) + (puppetlabs.services.puppet-profiler.puppet-profiler-core-test/with-test-logs [logs]) + (puppetlabs.trapperkeeper.core/defservice) + (puppetlabs.trapperkeeper.core/service) + (puppetlabs.trapperkeeper.services/service) + (puppetlabs.trapperkeeper.testutils.bootstrap/with-app-with-config [app]) + (puppetlabs.trapperkeeper.testutils.logging/with-log-output [logs]) + (puppetlabs.trapperkeeper.testutils.webserver/with-test-webserver [port]) + (slingshot.slingshot/try+ [&throw-context])]} + :invalid-arity {:skip-args [puppetlabs.trapperkeeper.core/service puppetlabs.trapperkeeper.services/service]} + :refer-all {:exclude [clojure.test slingshot.test]}} + :lint-as {puppetlabs.comidi/GET compojure.core/GET + puppetlabs.comidi/PUT compojure.core/PUT + puppetlabs.comidi/POST compojure.core/POST + puppetlabs.comidi/DELETE compojure.core/DELETE + puppetlabs.comidi/PATCH compojure.core/PATCH + puppetlabs.comidi/ANY compojure.core/ANY + puppetlabs.comidi/HEAD compojure.core/HEAD + liberator.core/defresource clojure.core/defn + puppetlabs.trapperkeeper.core/defservice clojure.core/def + slingshot.slingshot/try+ clojure.core/try} + :config-in-call {puppetlabs.services.request-handler.request-handler-core/ssl-auth-info {:linters {:not-empty? {:level :off}}}} + :output {:linter-name true}} diff -Nru puppetserver-7.9.5/.github/workflows/clojure-linting.yaml puppetserver-8.4.0/.github/workflows/clojure-linting.yaml --- puppetserver-7.9.5/.github/workflows/clojure-linting.yaml 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/.github/workflows/clojure-linting.yaml 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,30 @@ +name: Clojure Linting + +on: + pull_request: + types: [opened, reopened, edited, synchronize] + paths: ['src/**','test/**','.clj-kondo/config.edn','project.clj','.github/**'] + +jobs: + clojure-linting: + name: Clojure Linting + runs-on: ubuntu-latest + steps: + - name: setup java + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + - name: checkout repo + uses: actions/checkout@v2 + - name: install clj-kondo (this is quite fast) + run: | + curl -sLO https://raw.githubusercontent.com/clj-kondo/clj-kondo/master/script/install-clj-kondo + chmod +x install-clj-kondo + ./install-clj-kondo --dir . + - name: kondo lint + run: ./clj-kondo --lint src test + - name: eastwood lint + run: | + java -version + lein eastwood diff -Nru puppetserver-7.9.5/.github/workflows/pr-testing.yaml puppetserver-8.4.0/.github/workflows/pr-testing.yaml --- puppetserver-7.9.5/.github/workflows/pr-testing.yaml 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/.github/workflows/pr-testing.yaml 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,39 @@ +name: PR Testing + +on: + workflow_dispatch: + pull_request: + types: [opened, reopened, edited, synchronize] + paths: ['src/**','test/**','project.clj'] + +jobs: + pr-testing: + name: PR Testing + strategy: + fail-fast: false + matrix: + javaargs: ['with-profile fips', ''] + filter: [':singlethreaded', ':multithreaded'] + version: ['11', '17'] + runs-on: ubuntu-latest + steps: + - name: checkout repo + uses: actions/checkout@v3 + with: + submodules: recursive + - name: setup java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ matrix.version }} + - name: setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.1' + - name: setup gems + run: lein gem install --install-dir "target/jruby-gem-home" --no-document "semantic_puppet:1.0.2" "hocon:1.3.1" "text:1.3.1" "locale:2.1.2" "gettext:3.2.2" "fast_gettext:1.1.2" "concurrent-ruby:1.1.5" "deep_merge:1.0.1" + - name: clojure tests + run: lein -U ${{ matrix.javaargs }} test ${{ matrix.filter }} + timeout-minutes: 30 + - name: rspec tests + run: rake spec diff -Nru puppetserver-7.9.5/.gitignore puppetserver-8.4.0/.gitignore --- puppetserver-7.9.5/.gitignore 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/.gitignore 2024-01-15 23:29:55.000000000 +0000 @@ -19,6 +19,8 @@ ext/packaging checkouts .beaker +.clj-kondo/.cache +.eastwood # Bundler local state files Gemfile.lock diff -Nru puppetserver-7.9.5/.travis.yml puppetserver-8.4.0/.travis.yml --- puppetserver-7.9.5/.travis.yml 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/.travis.yml 2024-01-15 23:29:55.000000000 +0000 @@ -1,38 +1,44 @@ language: clojure lein: 2.9.10 -dist: focal +dist: jammy +os: linux script: - ./ext/travisci/install-java.sh - ./ext/travisci/test.sh jobs: + # NOTE: Puppet does not officially support the s390x architecture. + # Testing against this architecture provides value to the open source community, + # but we make no promises about its functionality and may ignore any test failures + # that arise when testing against s390x. + allow_failures: + - arch: s390x include: - - stage: puppetserver tests - name: "Java 8" + - name: "Java 17" env: - - JAVA_VERSION=8 - - name: "Java 11" - env: - - JAVA_VERSION=11 - - name: "Java 11 w/ FIPS" + - JAVA_VERSION=17 + - name: "Java 17 w/ FIPS" env: - - JAVA_VERSION=11 + - JAVA_VERSION=17 - ADDITIONAL_LEIN_ARGS="with-profile fips" - - name: "Java 11 w/ multithreaded" + - name: "Java 17 w/ multithreaded" env: - - JAVA_VERSION=11 + - JAVA_VERSION=17 - MULTITHREADED=true - name: "Java 17" + - name: "Java 17 (s390x)" env: - JAVA_VERSION=17 - - name: "Java 17 w/ FIPS" + arch: s390x + - name: "Java 17 w/ FIPS (s390x)" env: - JAVA_VERSION=17 - ADDITIONAL_LEIN_ARGS="with-profile fips" - - name: "Java 17 w/ multithreaded" + arch: s390x + - name: "Java 17 w/ multithreaded (s390x)" env: - JAVA_VERSION=17 - MULTITHREADED=true + arch: s390x notifications: email: false diff -Nru puppetserver-7.9.5/Gemfile puppetserver-8.4.0/Gemfile --- puppetserver-7.9.5/Gemfile 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/Gemfile 2024-01-15 23:29:55.000000000 +0000 @@ -1,9 +1,9 @@ source ENV['GEM_SOURCE'] || 'https://artifactory.delivery.puppetlabs.net/artifactory/api/gems/rubygems/' def location_for(place, fake_version = nil) - if place =~ /^(git[:@][^#]*)#(.*)/ + if place.is_a?(String) && place =~ /^(git[:@][^#]*)#(.*)/ [fake_version, { :git => $1, :branch => $2, :require => false }].compact - elsif place =~ /^file:\/\/(.*)/ + elsif place.is_a?(String) && place =~ /^file:\/\/(.*)/ ['>= 0', { :path => File.expand_path($1), :require => false }] else [place, { :require => false }] @@ -31,6 +31,6 @@ gem 'docker-api', '1.31.0' end -if File.exists? "#{__FILE__}.local" +if File.exist? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end diff -Nru puppetserver-7.9.5/acceptance/config/beaker/options.rb puppetserver-8.4.0/acceptance/config/beaker/options.rb --- puppetserver-7.9.5/acceptance/config/beaker/options.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/config/beaker/options.rb 2024-01-15 23:29:55.000000000 +0000 @@ -11,4 +11,5 @@ "puppetserver-confdir"=>"/etc/puppetlabs/puppetserver/conf.d", "puppetserver-config"=> "/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf", - :puppet_build_version=>"1d45c3bc74639a15ead4d14cd85a021718ad50c4"} + :puppet_build_version=>"018fd02451f52130ec12282a24c6b0f011a56834", + :ssh=>{:config=>true}} diff -Nru puppetserver-7.9.5/acceptance/lib/helper.rb puppetserver-8.4.0/acceptance/lib/helper.rb --- puppetserver-7.9.5/acceptance/lib/helper.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/lib/helper.rb 2024-01-15 23:29:55.000000000 +0000 @@ -62,12 +62,14 @@ # and the running of the PuppetDB test(s). def puppetdb_supported_platforms() [ - /debian-8/, - /debian-9/, - /el-6/, + /debian-11/, + /debian-10/, /el-7/, - /ubuntu-16.04/, - /ubuntu-18.04/ + /el-8/, + /sles-12/, + /sles-15/, + /ubuntu-18.04/, + /ubuntu-20.04/ ] end diff -Nru puppetserver-7.9.5/acceptance/scripts/generic/testrun.sh puppetserver-8.4.0/acceptance/scripts/generic/testrun.sh --- puppetserver-7.9.5/acceptance/scripts/generic/testrun.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/scripts/generic/testrun.sh 2024-01-15 23:29:55.000000000 +0000 @@ -39,7 +39,7 @@ set -x export GEM_SOURCE="https://artifactory.delivery.puppetlabs.net/artifactory/api/gems/rubygems/" -export GENCONFIG_LAYOUT="${GENCONFIG_LAYOUT:-redhat6-64ma-ubuntu1604-64a-windows2012r2-64a}" +export GENCONFIG_LAYOUT="${GENCONFIG_LAYOUT:-redhat8-64ma-ubuntu2004-64a}" export BEAKER_TESTSUITE="${BEAKER_TESTSUITE:-acceptance/suites/tests}" export BEAKER_PRESUITE="${BEAKER_PRESUITE:-acceptance/suites/pre_suite/foss}" export BEAKER_POSTSUITE="${BEAKER_POSTSUITE:-acceptance/suites/post_suite}" diff -Nru puppetserver-7.9.5/acceptance/suites/pre_suite/foss/70_install_puppet.rb puppetserver-8.4.0/acceptance/suites/pre_suite/foss/70_install_puppet.rb --- puppetserver-7.9.5/acceptance/suites/pre_suite/foss/70_install_puppet.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/pre_suite/foss/70_install_puppet.rb 2024-01-15 23:29:55.000000000 +0000 @@ -21,10 +21,10 @@ nss_package_name="nss" end if nss_package_name - if master['platform'] != 'el-8-x86_64' - master.upgrade_package(nss_package_name) - else + if master['platform'] == 'el-8-x86_64' || master['platform'] == 'el-9-x86_64' master.install_package(nss_package_name) + else + master.upgrade_package(nss_package_name) end else logger.warn("Don't know what nss package to use for #{variant} so not installing one") diff -Nru puppetserver-7.9.5/acceptance/suites/pre_suite/foss/80_configure_puppet.rb puppetserver-8.4.0/acceptance/suites/pre_suite/foss/80_configure_puppet.rb --- puppetserver-7.9.5/acceptance/suites/pre_suite/foss/80_configure_puppet.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/pre_suite/foss/80_configure_puppet.rb 2024-01-15 23:29:55.000000000 +0000 @@ -15,7 +15,8 @@ end end - config = { 'certificate-authority' => { 'allow-subject-alt-names' => true }} + config = { 'certificate-authority' => { 'allow-subject-alt-names' => true }, + 'dropsonde' => { 'enabled' => false }} path = '/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf' modify_tk_config(master, path, config) end diff -Nru puppetserver-7.9.5/acceptance/suites/pre_suite/foss/95_install_pdb.rb puppetserver-8.4.0/acceptance/suites/pre_suite/foss/95_install_pdb.rb --- puppetserver-7.9.5/acceptance/suites/pre_suite/foss/95_install_pdb.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/pre_suite/foss/95_install_pdb.rb 2024-01-15 23:29:55.000000000 +0000 @@ -10,12 +10,46 @@ on(master, "rm -f #{sitepp}") end +# Puppet pulls in OpenSSL 3 which breaks ssl-cert < 1.1.1 +# Unfortunately we need jammy to bring a workable version of ssl-cert into bionic +step 'Update Ubuntu 18 package repo' do + if master.platform =~ /ubuntu-18/ + # There's a bunch of random crap that gets upgraded in our installs, + # just upgrade everything before we try to install postgres + on master, 'apt-get update' + on master, 'DEBIAN_FRONTEND=noninteractive apt-get upgrade --assume-yes --force-yes -o "DPkg::Options::=--force-confold"' + # Install jammy repos so we can pull in its ssl-cert + on master, "echo 'deb http://archive.ubuntu.com/ubuntu/ jammy main restricted universe multiverse' > /etc/apt/sources.list.d/jammy.list" + on master, "echo 'deb-src http://archive.ubuntu.com/ubuntu/ jammy main restricted universe multiverse' >> /etc/apt/sources.list.d/jammy.list" + on master, 'apt-get update' + on master, 'apt-get install -y -t jammy ssl-cert' + # Once we have jammy's ssl-cert get rid of jammy packages to avoid unintentially pulling in other packages + on master, 'rm /etc/apt/sources.list.d/jammy.list' + on master, 'apt-get update' + + # bionic is EOL, so get postgresql from the archive + on master, 'echo "deb https://apt-archive.postgresql.org/pub/repos/apt bionic-pgdg main" >> /etc/apt/sources.list' + on master, 'curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -' + on master, 'apt update' + end +end + step 'Install Puppet nightly repo' do - install_puppetlabs_release_repo_on(master, 'puppet7-nightly') + install_puppetlabs_release_repo_on(master, 'puppet8-nightly') +end + +step 'Update EL 8 postgresql repos' do + if master.platform =~ /el-8/ + # work around for testing on rhel8 and the repos on the image not finding the pg packages it needs + on master, "dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm" + on master, "dnf -qy module disable postgresql" + end end step 'Install PuppetDB module' do - on(master, puppet('module install puppetlabs-puppetdb')) + # While we sort out a new puppetlabs-puppetdb module release, point to a branch that allows us to take the latest puppetlabs-postgresql module + on(master, 'curl -L https://github.com/puppetlabs/puppetlabs-puppetdb/archive/refs/heads/bump-postgres.tar.gz --output /tmp/puppetlabs-puppetdb') + on(master, puppet('module install /tmp/puppetlabs-puppetdb')) end if master.platform.variant == 'debian' @@ -23,10 +57,13 @@ end step 'Configure PuppetDB via site.pp' do + manage_package_repo = ! master.platform.match?(/ubuntu-18/) create_remote_file(master, sitepp, < false, + manage_firewall => false, + manage_package_repo => #{manage_package_repo}, + postgres_version => '14', } class { 'puppetdb::master::config': diff -Nru puppetserver-7.9.5/acceptance/suites/puppet5_tests/puppet5_version_test.rb puppetserver-8.4.0/acceptance/suites/puppet5_tests/puppet5_version_test.rb --- puppetserver-7.9.5/acceptance/suites/puppet5_tests/puppet5_version_test.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/puppet5_tests/puppet5_version_test.rb 2024-01-15 23:29:55.000000000 +0000 @@ -8,9 +8,9 @@ assert_match(/\A5\./, stdout, "puppet --version does not start with major version 5.") end -step "Check that Puppet Server has Puppet 7.x installed" +step "Check that Puppet Server has Puppet 8.x installed" on(master, puppet("--version")) do - assert_match(/\A7/, stdout, "puppet --version does not start with major version 7.x") + assert_match(/\A8/, stdout, "puppet --version does not start with major version 8.x") end step "Check that the agent on the master runs against the master" diff -Nru puppetserver-7.9.5/acceptance/suites/tests/authorization/default_rules.rb puppetserver-8.4.0/acceptance/suites/tests/authorization/default_rules.rb --- puppetserver-7.9.5/acceptance/suites/tests/authorization/default_rules.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/authorization/default_rules.rb 2024-01-15 23:29:55.000000000 +0000 @@ -46,7 +46,7 @@ def report_query(node) curl = "/puppet/v3/report/#{node}?environment=production " - curl += '-X PUT -H "Content-Type: text/pson" ' + curl += '-X PUT -H "Content-Type: application/json" ' curl += '--data "{\"host\":\"' + node curl += '\",\"metrics\":{},\"logs\":[],\"resource_statuses\":{}}"' end diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crl puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crl --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crl 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crl 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,12 @@ +-----BEGIN X509 CRL----- +MIIB2TCBwjANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRlcm1lZGlhdGUg +Q0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLm9yZzEZ +MBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2VydmVyIE9wZXJh +dGlvbnMXDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFowFDASAgECFw0yMzA0 +MTIyMjQxNThaMA0GCSqGSIb3DQEBCwUAA4IBAQCSrVWjsTA8YeO27GH/RIb/JJ+8 +jxAdKDlm1Fu96WkJriAmOHp08cByiNmVsyFvKClCeGHzXfFM5sUdWlfaCftK4hXd +Mva9ErcDgYILuJJoCAqWUj4/KmA6kJbxpeBx/kh+FUfe+ptTu5aRrECITEozf83p +0QfKGdE3i/Hts9kC8bWR/rcYokUhkjlCZW+WvMAH+a5FjvkessfG1YrVESYSrp27 +sqX6fdw9vUQiSRZnzP6cfIqEzkJXBqaMXaHrzCjU2GjOCGakjQVSmIs4jgHRLr8h +FFTBEnNCiL0THWGjR422M5IA+enyZi8OOn7f2c3tvptitAF+4kfl8LTiBQfH +-----END X509 CRL----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,23 +1,23 @@ -----BEGIN CERTIFICATE----- -MIID3jCCAsagAwIBAgIBATANBgkqhkiG9w0BAQUFADBJMRAwDgYDVQQDDAdSb290 +MIID6jCCAtKgAwIBAgIBATANBgkqhkiG9w0BAQsFADBJMRAwDgYDVQQDDAdSb290 IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBs -ZSBPcmcsIExMQzAeFw0xNDA0MDgwMTI1MzZaFw0zNDA0MDMwMTI1MzZaMH0xIzAh -BgNVBAMTGkludGVybWVkaWF0ZSBDQSAoYWdlbnQtY2EpMR8wHQYJKoZIhvcNAQkB -FhB0ZXN0QGV4YW1wbGUub3JnMRkwFwYDVQQKExBFeGFtcGxlIE9yZywgTExDMRow -GAYDVQQLExFTZXJ2ZXIgT3BlcmF0aW9uczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBANAjnBQPul4VZp8/PgnQxZtJQHhgWCRtLw5KDoZHFfQQxGW6utHb -MPIoX4qgJDb8msojb7ZO63C2BAjO5FHwhAwk3SciZX7VEt5YUYg0X1J7GyWHKWEt -yXEiIlXZ0xfXdzZ0kPskITQTLmKav7d08cN8SSqhAMeWhbiZ9xaCFWnYneqGdHc/ -Ps8EPszuJTiwrJsQtoxXFEdZfnJctlleGyZZFk/zg4M3P3RWr/ATBnMqL1Q4VfTd -9C23p+6kYhrYMxfWrawWAqyzn/G17X1TzQY4qW9Imn+RYLEQeBkO+KTl0Y+eaIOD -1PLfGaUu+XUumcMcbqyYgM5heqPEKHMs3g0CAwEAAaOBnDCBmTB5BgNVHSMEcjBw -gBQWr4Al4/rqSL+RM2YB/VHvJGdMY6FNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEa -MBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3Jn -LCBMTEOCCQCxuQRy+xEn4zAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAN -BgkqhkiG9w0BAQUFAAOCAQEAqyvoi3vDeE0Do7k2QiVF4WBhfGEW6921+UMVgMqB -SSV4mJ98ep4lrJA4VZPEW7jZWbox8fpH2WmA4DSK6lBMf7MoLSuDxaTmVDCvauGU -jtOD4ejIKWcJN8tkyFjz7DCca8x7EryZpr5sZMU78jZ/jOVwIK85FX/5ptQdoyo+ -j7TxWz464bUrlCOyzZEKIDViFeahY6Krfsfn60lmaWjXD5WSc9g5V/RA59cECpiT -Dl9Li9Weu0aXoF8nmWVhhBI1drmqvKbffAvQ42K4x2OTFG3r5wbCRSZTCGT2BWZW -M2HXCE5pMoTvM6H4PbMJsJw/x4qonM9HG81EcjHtKDUFGA== +ZSBPcmcsIExMQzAeFw0yMzA0MTIyMjQxNTdaFw00MzA0MDcyMjQxNTdaMH0xIzAh +BgNVBAMMGkludGVybWVkaWF0ZSBDQSAoYWdlbnQtY2EpMR8wHQYJKoZIhvcNAQkB +FhB0ZXN0QGV4YW1wbGUub3JnMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExDMRow +GAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJ1EAzT0LLWHkeo8meKuMjMR2RI53lHwkSWNohBOPVA3lY+5UGei +lyrkIb9H22ZR8DnNz7JjkJkC6Uu55PwFh2d2Q60wZLqlX21jRvmsCzfdGt/brD6Q +q43mAJp7MyD3+31lmv2wmKvMPWoq6u+tjjDbtEey7HAiJxQ/KCGPqcUv5RFmqKz2 +pfteWFxqHQUmn4JABZpte7Fl4/dQyw/6r7ctEnKqVuF2lQzCS0awy3oYQr6Qwwbs +HNTApjmW2EXTuWYTYQ/ftQeGN5QdQm+9fiKi45Z/HsY1z7vyUY5TjjSLITV0a420 +v2WhW8ckMWrllwaCEzmfNcjx9W5FQm4w8+MCAwEAAaOBqDCBpTCBhAYDVR0jBH0w +e4AU9ZCGS16tJfUQP94+xtSfQj+Hqq+hTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0Ex +GjAYBgNVBAsMEVNlcnZlciBPcGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9y +ZywgTExDghQvFOXf1u6BVLQnDLhlalXcghZONzAPBgNVHRMBAf8EBTADAQH/MAsG +A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEALw/RGiXdEZQ+rj43BqeOlAas +mUpHnhmc9XPVNtkgRTf9DYc/NxRFBLNC8eBnThcZGTlN7c025xYkjQsIvs6gKD+y +SKFLv0+eHuCywrDgAZCAcFC+ekXu5b7BfKMfjkWJHELYEIomgVHY4mj6blZ5lmWk +Mmr8qYzDAWYD6Rits21YMKyMWcAcoTed9GQFwZOhLJHAG4lvoRd6qXAK+U7dIoQh +vf5CGFP2YO71un22IGRy5QJhoDyxV3yHbbiv7zS+NPn1bOQOfKRGu58PxfsyO3BT +aWGyMzLhxPB6ufAQo7fWX1jOqPxJ1aMS9+aAba7QW/QrOX+LAlAulS034izUFA== -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/ca-agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQI5wUD7peFWaf -Pz4J0MWbSUB4YFgkbS8OSg6GRxX0EMRlurrR2zDyKF+KoCQ2/JrKI2+2TutwtgQI -zuRR8IQMJN0nImV+1RLeWFGINF9SexslhylhLclxIiJV2dMX13c2dJD7JCE0Ey5i -mr+3dPHDfEkqoQDHloW4mfcWghVp2J3qhnR3Pz7PBD7M7iU4sKybELaMVxRHWX5y -XLZZXhsmWRZP84ODNz90Vq/wEwZzKi9UOFX03fQtt6fupGIa2DMX1q2sFgKss5/x -te19U80GOKlvSJp/kWCxEHgZDvik5dGPnmiDg9Ty3xmlLvl1LpnDHG6smIDOYXqj -xChzLN4NAgMBAAECggEARD2Ym584fEZJ+iYzAebYEvymTZFQ9MhzaBzxvCasVPP2 -YGAjhlB2ML757CprFTgmy+VoZ/5iBPc4RWcHxrGzqYOgmocVfcsAP7P3L0/0fMdt -9BTnhTwM0rHdTgZ3xlZXeJwpOJ304Oz1BVE1UEHgTjZ+iqJ07fs05nxcXZ3SxXuu -yte/CtOxfiu12qw0Pa3en/wvkqeHPMrPYLD4PJznPIFmcfzVH6qO4DWtblW4HHLW -OqoqgjpuDLe1hjN5RZY0dLnvuKAr1d+ZKhvvuPPKGUR9J2/h/vV1ZMJDW45zJIu3 -XfoFUxxnrSBynBtyfFwkC2Btriryuu9HdfnMxCHpYQKBgQD/r0f36Zj7M571E+5Q -owCAE0qHRd/++p+KA3x/mVnECQ4c42QVwAwW99wd062k/9IbzVS6okbz1tKNsSvS -7TgWc1qAWeoxV8y2Fo3ovs8mOuRxnKMjWwlf9vOVEr49r1h/CP9iEzcjW147hw0m -EyWdBFBLQVLi/XdbguJW9e0bxQKBgQDQZVGM88r60yzo50kFLx4gVPx7nH8LDLkM -HM6Lxk5UQxbHZEzpGpxM+GEypHdRb5d5uITTzZSmosRuUc73mvTJE8hc5kcSZPsh -pEpSS4El7gcp/cmDNrHpqJdW4VwftJ8WYwFNOCCgLcmSNLJovc11j5NtGeP+Leqh -EsjWXOr1qQKBgAxuBv+kWY2MuuOLLoC5C+MuDOd6nCMXJ/5boQfK+rQvBIKfA1ST -W4MaVZcVnVFyJlK3rrDMBsr/3IiK3miIo7tjrDilJl9ztz365rcz33oqTsS/Kqcj -W9dQeBL9MEZrac/zLgcki/+qB3C5Zgg90gxKE2U1LcRfMhg+yqYTmo1JAoGBALWf -J+TdcJELzP8q26Pt/aaWCvo8WSirLPdWf9inuwqK8eZTDwi1jXUzn5qAZhEOXYjS -/MiPSje0cdfn6qY3YZGBcUUt2NE6OviF89QnQ+ZnvcymB6MY3xPSQBuTCzQCugfL -v42qFh0j6qJG1RqeGNuVhxo1z1NudydsdKcGkiwJAoGAYNbfuY3ccWmvX3K9ufL9 -M8m9ADXVE0o9LUQzZZwdv9IsQoeyufR7q0NUxrLqsracbJVhIoRzWZ7AUsWZ2qiX -eME8gioXoJVShTw9TpZY/nuH72iP/SbpZ9s+/43wNP0PTCS2ZQKwxmszz8Eg3qxN -D6ThCdnUCDA4JNQou0GozRQ= +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCdRAM09Cy1h5Hq +PJnirjIzEdkSOd5R8JEljaIQTj1QN5WPuVBnopcq5CG/R9tmUfA5zc+yY5CZAulL +ueT8BYdndkOtMGS6pV9tY0b5rAs33Rrf26w+kKuN5gCaezMg9/t9ZZr9sJirzD1q +KurvrY4w27RHsuxwIicUPyghj6nFL+URZqis9qX7Xlhcah0FJp+CQAWabXuxZeP3 +UMsP+q+3LRJyqlbhdpUMwktGsMt6GEK+kMMG7BzUwKY5lthF07lmE2EP37UHhjeU +HUJvvX4iouOWfx7GNc+78lGOU440iyE1dGuNtL9loVvHJDFq5ZcGghM5nzXI8fVu +RUJuMPPjAgMBAAECggEAXeXv7DwmPbTWYH8Seo/7lRaA/yriHRZ/82Yfb5Dndu/Z +Mtk4ssgXbhqMM8NZsjwWLM0/x4k/GrzsQrSZjghXEmVAfzW3zuFSmuN9yzD3+cDg +fOySaUDDzZOYpKQOoQ6vb+2snvBEF794o91ALR75GR71AyJHnaQmpSYYdgFrIjpI +3At32/zOpy/grTpq/2bsHmht1KsfrapXKiz4/cz9c/qUmgjxEj67A59tfvcpB2Ps +s2jGHsTB3X2x0FKurKJThsgf+F1b96JC/vamzJsS9P5BdXV4aL5MxcGbzrNVsWE0 +ca1W3Txltj6mOcOyliwYU2aJvynW7a49/aIdiJjd4QKBgQDOlRWgseev3jNfAhk5 +mDV+rCU4yjDeKNrfc1UAU9BsSzbv1LPCUgIY9BeB+9sH9cZqoD7atX91yc9COmYr +BXa8XBTmwL/l1lQgTAyRHFjSzjStwrvLFS4CS7oyh0VVfZ07skprU3/R/kEqQ0Ji +W5ULnLEnEm91MUug2JI9VFrH+wKBgQDC4tGt8v51IEdNlYrxd2xV/YZkoHITpI6Y +7kF1sXZG0T/CJ0af5Z5LfBtsmThyn9SSXmy/P5+m77rYcxcXAWt41pLq4rxj9RwX +dOiM+0unhZF7vXKb5BGefZAhiavqGQFoJKR0vevKmDfZvbQQE/fhUSVgQSzcKi0n ++u8fdjK3OQKBgGDiapJC3XYNr+oPAeWRDQWrlolbyi9m2b+SR3sv07/2Rn2UM/oe +m/03/Q5pIABLtBKttMy98lk7nIhi+yeSGG2ege721wjjy/CZugsUUFQNgYc8nPZR +qJJLEbhA2LOPhf5JT72mG3xQ0h8QDEI00WHwyuSYMM21oNJ+v5cjyK+hAoGBAJ/x +/frQsow4SKzhLd6Nj+lnIJSXpeh/JtVWvXy1gAgRFk3nZCw5DNiQ7AUOH2jbyl+y +hXfU5NfiaqvYkTu9W5IaMQv7uHglI8AQoAeM+wNrOQKgcl6FKPy1usP7tI91pFTv +tdVmRg3JLhVTf2Our8NJkO5Vr1ametcUOIrOvELpAoGBAK6UhYxGdO79MC+NUxdA +iArCtuajvpGCnFYVbgkgRGaw2NV+GzcTbDXded9D+8K78k2Cmfr6ENv1qpOl7rJw +5Z8n4XfrlHjzoTA02yR77Ap2FLeqDrNZb29+JsKH5qCJlJdqnbYyN/LudbQud0BD +ey0bJbXH61JPvr6r6bIuUsQ1 -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/01.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/01.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/01.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/01.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAd +MRswGQYDVQQDDBJhZ2VudDEuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDa8oNyLxOwgoJa0thHlB3CPLAm2mcrStPD0RHmHxlRihsa +3BrGdvOekD79eBi+7RmIsnRpUL0F+7oCS7skQ4z+TkS3hgoXyOgTgK8UWY3osNny +B/Y/61KIzCIlZXXey2sQYx6jsaxxsyfrs/ErdFFYbEE1MLvRJadSw3MTcML8WhCH +S0jB2DVp9wmKDGWtWHBRFpvkxwGt2u4Zlux9iVOz4XPNxxh19W+vFBms9DQDmJmN +c3X0ZefKlrilKdXMg2BkFEIawMRVJgfNvb5uoAjik6KQdFG3Vnt9VXMwDmO5DmXT +wElOKAwqouGeF9qp3t6DPLOylyIYjeC0uPbCy6ETAgMBAAGjgZowgZcwWwYDVR0j +BFQwUqFNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9w +ZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/ +BAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC +MA0GCSqGSIb3DQEBCwUAA4IBAQAmSiSyp5nlk1Cwn3Rw23bqCIQ/WlZzJE/Uh1+Z +TuiwVbZknaJ0gsZELmkFGsvX56OS0nTPUyl6m3Ct5vCAX33nhOH94njX5yFte1sO +zT3AkJZqz4elf5Li+aGvgLplN04XH5snnw23c2cwhbTu3GwZok07MpUp09jonNfK +RfV295tTfC/L7+XXz9uOPwI/oCNiZNTbpl+pshXaG9t2vtDiIgQUWF4jXzWpUu9I +wFo48IM1OVQrujb3LliD4fLZItfJK/xJKIKixTxrPy0Xu5zdz7DyTBJnvZYiUVtU +8CKZR+5wtc8aKdDxfNaY7nORiZEyd/uprVb6okCpZnVxxXum +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/02.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/02.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/02.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/02.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsDCCApigAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAd +MRswGQYDVQQDDBJhZ2VudDIuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDs8wg1+xrBWHcp9d4b3dbIqHQiuYtKbXlGU52lKoU3vArM +b2lu39EWMXrxg9Dn3joI3X8nUynGlB1mHvog6/RXggw2T5WtPIviAMIR3niQWS3g +YrgpT90GVeIPIiuVh76IUFnz+a9wfxZ3rsXRU74kXOzWXVZr2pSH1HjSpWmR6Hgj +HUPi7/CRPL/V+/nvyY66cvxqgtOchGYhZO3+0zeGnXzVlXwdleCQnJ72EAOIyvQh +rJhKiHvYw9/ZTD3VMHntBUu/QFB6a9ILKxggB3iOQXE2HwPBqO+cJfoi4qTpywE7 +MOdI0q6MK9pr6r8TAGAlEPYoImbUFHIehJmPvSZDAgMBAAGjgZowgZcwWwYDVR0j +BFQwUqFNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9w +ZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/ +BAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC +MA0GCSqGSIb3DQEBCwUAA4IBAQBPtNXbJI3C0/8gHvB2g6tfSGBHVvOH1lRpQ8FY +EYW4m8EubV0cFdDpd02/gG4hOiCYFOOcMFENGOTRGSIMDWUED7doNttxHFWxVOEX +6M98cTlTpWW3PThCru5Oa4CGFcGFbo8Khb4vvZv5vIwf8ShUe2sl0I3REZXUx5ad +fQc4sXiiPdf/P1D9ppulz/UchMfg9sg0ZRjQmVmCBZvA4/P+rBS+T+nJQ0bfpTmV +EOVG149Lt+61kotJmxv5/J2UYRneWBmRE7P8d8DTqhOHQI5cHICgVq5Jw9m7nGlS +LB7F4lKYXXU/9Z5JVKtM7/m4+MfCHlQyocd0cL9laKseE2Ek +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/03.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/03.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/03.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/03.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsDCCApigAwIBAgIBAzANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAd +MRswGQYDVQQDDBJhZ2VudDMuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDImAML5qGenaZ1oXlNyDTnPg/0bq5ft2jTZjPTUijBiuYI +ZqANvqjf+bicC+9LdQwpRoIm4hS+cnvaEdXSV73cWjgFzAFJTBYaRXLst8/r0QlU +BNkHgDpgCFVwR+YIEmHeesprzId/uDE1tf13IRI1NpDgU+jlBH3wOiUDr8QbPWPG +IUV4bbEzLCGoP2fO5Le5p8+NdRI7XCHo7ac8eRL90pyBFBKzdc93cYMI7PvimuX7 +MiVObjil3FF+tsdkAAAJezLKlW2CKjRUdHCr5/2WIRpjiIs4k/hCDToBm+8ye9m4 +3NWn68XMIOjCiXl8wjdbTA3z2A0iRE9hGh+pYhQzAgMBAAGjgZowgZcwWwYDVR0j +BFQwUqFNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9w +ZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/ +BAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC +MA0GCSqGSIb3DQEBCwUAA4IBAQAJvTvSc/J7QUZXtwIDoRSpEw1BBb+AKsa4D4ui +qQXxiiRAe+HkSVvDnYU6gDTiEwtjccC/78KYti6QKWsYXhdW298ALdP9guAL78Lt +A1olS9hNLXMiqWWEGPu0UAmNuTpdiwYbTL6vb7tcgBsnXONS1N8i7N1SRNyGRURL +E2h5n8LGupXtum71XKMpNDT7XKKh75O8jsfGwkl2g4DQKmU5+9X2RXX+btooIsBi +xgsOcq6Tb1qHdZRvqH75iypj8osiy+4cflgvdjuElHBvIlFRlyJyJPhAPk1xWNRq +5+tDXG9cxNfWCGp2rCtVTxx2LGkCj1SQulMc6y6OZin+J/QQ +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/04.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/04.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/04.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/04.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8DCCAtigAwIBAgIBBDANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAe +MRwwGgYDVQQDDBNtYXN0ZXIxLmV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6RLLxx3df3Ln6TG3iORiJNsQfZsRa00XgGhzbdTGOJ0v +X5Yjq3igQeGVd3aCqAEivaDmk0Mk6mWyH5l1ODn75PdYUFMVblmNaCZ1sY6lc3uJ +qWPEz6AZYojkLrhumtInUD6y9N3q2CllgT/B1yb4Hfs2WsTGtZjmYOqdKv0gaTw0 +p46Uj0UrE42GOVjrrIQfomq16XwUDh1kqfIxuob4J/ZHN4pHsFtIvh3F3ZD2OGPi +YLyPGyP3C1ssUnCoqfMM2TUO9LGeWquRvCLoYe+jA5Q8lbDU1NjxrRlfS+LyeCgn +96vWWmh/THVnaCiSj4O68pZABsgmHhg+diC8t+/sbQIDAQABo4HZMIHWMFsGA1Ud +IwRUMFKhTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0ExGjAYBgNVBAsMEVNlcnZlciBP +cGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExDggEBMAwGA1UdEwEB +/wQCMAAwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD +AjA9BgNVHREENjA0ghNtYXN0ZXIxLmV4YW1wbGUub3JnggdtYXN0ZXIxggZwdXBw +ZXSCDHB1cHBldG1hc3RlcjANBgkqhkiG9w0BAQsFAAOCAQEAP5IbZHIFdRjh7f1p +9SHcGEJ9O/OYZfkmqzKfNsnQ3q/Honz3C9SxWkAdOTSQClayi/x+RkOL0h+50+Mw +X/rC3qkIRce0JV+Uym/T4WsGz4Oko37xlt6Mlm9x194mTk3wcy22kY6jEOfp+YO3 +Chsdw62yyp09v5YkwE9obPfSB3LshgabLBDsZPOqRM1IFmpQ7gkuwa/RlvQfidrs +G9A4EzbWAt8d7v59HsL9u0+p3NT7JM1IH3pU0u5UWQwrMVkpiurtrz+ysTXp8XPT +QUaA2LkGb4omiW7n1FsablFS6uByD7v41P7oDW5dMkOsKl+mw1Qeh/wZOplmoQVx +6xT00Q== +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/05.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/05.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/05.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/05.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjBE +MSEwHwYDVQQDDBhhZ2VudC1lbWFpbDEuZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDIKXR0WvZcBrJPE8JFaKB8/CMi0jUshP3f38O4YcBh0nF8AcAwwC9/iLFe +xXHY27lRO1V0XpvebRzlFDMSoeqS5wGYge7ola9t5nR0njwWAfsAIe/dt9ZdJuxn +mk56IQV/cQXw8h/qHSZP2/utmHycWRkHY5U4VcAZX0IQwLBqk6+/ywxtl9MvIOuY +8yImz7BNwnm2ZfWhHm3EHqF7hOhPNnaOq0R4y0TCd+gUfdSF5t1KIQ7dLyrw2Bvd +c472rGpM89oQRuw6YvYe8/K4XLbtyZs3FwAOUW5ijJwUIRQMPVuJj60TQHcH9mIm +byL+ZCOI9DQgr3Swx05wMNddXSjpAgMBAAGjgZowgZcwWwYDVR0jBFQwUqFNpEsw +STEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMx +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/BAIwADALBgNV +HQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3 +DQEBCwUAA4IBAQA4q8Vg2LMKmvDy+Q+lBIZh6rbcTafMu//H9+AhPcGh1gR0J0Wj +SDps/woYSle04b7MvwC6+yPCiT79nIpZBQ5jHIcWxnE9tQa7DcPuTyPyalNUlzuE +1Pp2cYkFiYMv2lGU4z13y8PynoL+vqoij0LPXa6SP4lhmbvC/qWkf8IF6C6kGTWB +ZQTTOa0/MWirGlKDsUh4UygzpRICjO4ouzBKDTEmfLEZKpWiCY3WtrCWO6zK6OOa +O85k5MnBXjV/Z2Yv9MRABZDjQaFtJ6zTktlakjOyqoPMaPu5aV7B2T1fN4myM1NB +m+vtQqWUNYDOpTGk7QML5Bfz7LH/c5NohQbq +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/06.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/06.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/06.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/06.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIBBjANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjBE +MSEwHwYDVQQDDBhhZ2VudC1lbWFpbDIuZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCl93N93Sxm3Odr8PUOV7iQDDFy3399SkTSq6jGV1P9KIh2hszlSoLKYhKA +PVlIlYvCcbVflsixHDcyd/8SywstNndhdIhM1mpi6QBhEtnOoFjfYTfbVKSC6PUl +PoUIVqwumiB2VLiHCy5AHij1Ak7EMD/4gpvvVfGoG3j9jLNl+csu79j2yQYiDk9a +wQkoI4dO4jBAdq983Hz3lfKFj1qgrwUMSpMsN3KMi/6osJTzdOFFe8UuizHQFrnh +4Y9/26zZGdgOd31kmb0Vka1c3+TIwr9JbiQqWo/ZYSX6j187aRpPCl6tvUqVPSO1 +TPSCKHrkVauR8ZsuwgOGo+mR31VLAgMBAAGjgZowgZcwWwYDVR0jBFQwUqFNpEsw +STEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMx +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/BAIwADALBgNV +HQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3 +DQEBCwUAA4IBAQBeSAbhGhWQCclBrX8DYvQa+HvZIcsw18UILA2zuGvNoUgbWjob +3w8nMYSjQ6YGJns/H8jHOTShut8wsrKCRuEzI80LzYUF6MgpEDrxwlxbKQLBHg3K +XnCNSEwDYVJOllIUEAK0TByPP1YFHtd2IdZ9kTticVJdmravZFM18SbLt4J6X/t/ +EBuiJE27fdMqJSv9OP6PUg+MYakvBz3EGwzbjwn7NAB8OB+PPXd14Er2P8B1C8of +Hubiv5Hk3NX5QbXGkoetjzRLkpXCaOZNkWPcWxkPMv7cVozpDtm7E1UC2i7BUdPh +FAPv+O7WxDixLb9O5uuE4jnszg4Jhp7JfOqU +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/07.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/07.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/07.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/certs/07.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIBBzANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjBE +MSEwHwYDVQQDDBhhZ2VudC1lbWFpbDMuZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCqcD47G2JXhzySuwJX+2jkDW1CFe7WrmWtTaeneszgSz2juuFA39KGS2lC +/RBU6okPwhYoaCFLZYTeGx7NNaUYc+GJf//IMerzqbXTLsjh96686+7Iwqjn+B5O +Z3XfCF2quEC1g/uT+/ziqN1YmRMzMGXMlSQGp17sCP0EqW4Xcry9fLhCV9Z9kJyJ +fxqIG8fmjCyhUwL3x8iLmaRVFovhfLvyNPLLYkOcpDdeBuASn/7/rtV+bWfVWd/Y +a5wNqvIbtlp/FUt7VZ35B66SXp3yF1kaVl159BbkL2xzThch+0rRUH6OD3UyOv8Y +Ubmkl2Q7ykNJNh2nKwtNIqPt5PZ3AgMBAAGjgZowgZcwWwYDVR0jBFQwUqFNpEsw +STEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMx +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/BAIwADALBgNV +HQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3 +DQEBCwUAA4IBAQCMrpNT6pqqkKn+usB8sFWIcASP3OTnaEQVCpnoCoudrB4UHDZO +oQzzY3Ys5fcTK7rnXS7J1daS6Qi1g/XSiTCB8xVWXjz8uQtXGQXjVWNpxI9clFyo +IJBZ7Cm9h8QatFIF5G+e5E8G7HEe4Cx88ESAAuQ2cnJsARRB7DyEuegPqZunGc1M +T6Z5Y4Xn45zorp+TLLjE5zbrInrNeaVzcqFQYpMbzvFoWwba8G/1YobKggNIgLk5 +rlqUKrZKawRjtMHBnIJf74RLBZ8qQezycIUso8vTi3is1BdRgSfPEdlWAxIwyOoj +uSnhq4DT4QfDZK9u1J9e7pxthhQdEW9Eo6/v +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,7 @@ +V 430407224158Z 01 unknown /CN=agent1.example.org +R 430407224158Z 230412224158Z 02 unknown /CN=agent2.example.org +V 430407224158Z 03 unknown /CN=agent3.example.org +V 430407224158Z 04 unknown /CN=master1.example.org +V 430407224158Z 05 unknown /CN=agent-email1.example.org/emailAddress=test@example.com +V 430407224158Z 06 unknown /CN=agent-email2.example.org/emailAddress=test@example.com +V 430407224158Z 07 unknown /CN=agent-email3.example.org/emailAddress=test@example.com diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.attr.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/inventory.txt.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,7 @@ +V 430407224158Z 01 unknown /CN=agent1.example.org +V 430407224158Z 02 unknown /CN=agent2.example.org +V 430407224158Z 03 unknown /CN=agent3.example.org +V 430407224158Z 04 unknown /CN=master1.example.org +V 430407224158Z 05 unknown /CN=agent-email1.example.org/emailAddress=test@example.com +V 430407224158Z 06 unknown /CN=agent-email2.example.org/emailAddress=test@example.com +V 430407224158Z 07 unknown /CN=agent-email3.example.org/emailAddress=test@example.com diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/openssl.conf puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/openssl.conf --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/openssl.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/openssl.conf 2024-01-15 23:29:55.000000000 +0000 @@ -5,15 +5,15 @@ # Root CA [root_ca_config] -certificate = /tmp/certchain.KDOYxTc2/agent-ca/ca-agent-ca.crt -private_key = /tmp/certchain.KDOYxTc2/agent-ca/ca-agent-ca.key -database = /tmp/certchain.KDOYxTc2/agent-ca/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/agent-ca/certs -serial = /tmp/certchain.KDOYxTc2/agent-ca/serial +certificate = /tmp/certchain.HHLFb2ep/agent-ca/ca-agent-ca.crt +private_key = /tmp/certchain.HHLFb2ep/agent-ca/ca-agent-ca.key +database = /tmp/certchain.HHLFb2ep/agent-ca/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/agent-ca/certs +serial = /tmp/certchain.HHLFb2ep/agent-ca/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 policy = root_ca_policy x509_extensions = root_ca_exts @@ -31,30 +31,30 @@ # Master CA [master_ca_config] -certificate = /tmp/certchain.KDOYxTc2/agent-ca/ca-agent-ca.crt -private_key = /tmp/certchain.KDOYxTc2/agent-ca/ca-agent-ca.key -database = /tmp/certchain.KDOYxTc2/agent-ca/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/agent-ca/certs -serial = /tmp/certchain.KDOYxTc2/agent-ca/serial +certificate = /tmp/certchain.HHLFb2ep/agent-ca/ca-agent-ca.crt +private_key = /tmp/certchain.HHLFb2ep/agent-ca/ca-agent-ca.key +database = /tmp/certchain.HHLFb2ep/agent-ca/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/agent-ca/certs +serial = /tmp/certchain.HHLFb2ep/agent-ca/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 policy = master_ca_policy x509_extensions = master_ca_exts # Master CA (Email) [master_ca_email_config] -certificate = /tmp/certchain.KDOYxTc2/agent-ca/ca-agent-ca.crt -private_key = /tmp/certchain.KDOYxTc2/agent-ca/ca-agent-ca.key -database = /tmp/certchain.KDOYxTc2/agent-ca/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/agent-ca/certs -serial = /tmp/certchain.KDOYxTc2/agent-ca/serial +certificate = /tmp/certchain.HHLFb2ep/agent-ca/ca-agent-ca.crt +private_key = /tmp/certchain.HHLFb2ep/agent-ca/ca-agent-ca.key +database = /tmp/certchain.HHLFb2ep/agent-ca/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/agent-ca/certs +serial = /tmp/certchain.HHLFb2ep/agent-ca/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 email_in_dn = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/serial.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/serial.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/serial.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/agent-ca/serial.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +07 diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/certchain.sh puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/certchain.sh --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/certchain.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/certchain.sh 2024-01-15 23:29:55.000000000 +0000 @@ -9,14 +9,14 @@ # emailAddress. # basic config to describe the environment -# B="/tmp/certchain" -B="$(mktemp -d -t certchain.XXXXXXXX)" +B="/tmp/certchain" +#B="$(mktemp -d -t certchain.XXXXXXXX)" HTTPS_PORT=8443 OPENSSL=$(which openssl) # utility method to dedent a heredoc dedent() { - python -c 'import sys, textwrap; print textwrap.dedent(sys.stdin.read())' + python3 -c 'import sys, textwrap; print(textwrap.dedent(sys.stdin.read()))' } # invoke openssl @@ -142,7 +142,7 @@ default_crl_days = 7300 default_days = 7300 - default_md = sha1 + default_md = sha256 policy = root_ca_policy x509_extensions = root_ca_exts @@ -168,7 +168,7 @@ default_crl_days = 7300 default_days = 7300 - default_md = sha1 + default_md = sha256 policy = master_ca_policy x509_extensions = master_ca_exts @@ -183,7 +183,7 @@ default_crl_days = 7300 default_days = 7300 - default_md = sha1 + default_md = sha256 email_in_dn = yes @@ -272,7 +272,11 @@ local dir="${B}/leaves" local fname="${fqdn}.issued_by.${ca}" - [ -n "$exts" ] && exts="-extensions $exts" + if [[ -n "$exts" ]]; then + exts="-extensions $exts" + else + exts= + fi mkdir -p "${dir}" ( cd "${dir}" @@ -281,7 +285,7 @@ openssl req -subj "/CN=${fqdn}" -new -key "${fname}.key" -out "${fname}.csr" CN="${fqdn}" SAN="DNS:${fqdn}, DNS:${fqdn%%.*}, DNS:puppet, DNS:puppetmaster" \ openssl ca -config "${B}/${ca}/openssl.conf" -in "${fname}.csr" -notext \ - -out "${fname}.crt" -batch "$exts" + -out "${fname}.crt" -batch $exts ) show_cert "${dir}/${fname}.crt" } @@ -310,6 +314,17 @@ local masterdir="${B}/${ca}" local dir="${B}/leaves" local fname="${fqdn}.issued_by.${ca}" + if [[ -n "$exts" ]]; then + exts="-extensions $exts" + else + exts= + fi + + if [[ -n "$exts" ]]; then + exts="-extensions $exts" + else + exts= + fi mkdir -p "${dir}" ( cd "${dir}" @@ -318,7 +333,7 @@ openssl req -subj "/CN=${fqdn}/emailAddress=test@example.com" -new -key "${fname}.key" -out "${fname}.csr" openssl ca -config "${B}/${ca}/openssl.conf" -name master_ca_email_config \ - -in "${fname}.csr" -notext -out "${fname}.crt" -batch "$exts_arg" + -in "${fname}.csr" -notext -out "${fname}.crt" -batch $exts ) show_cert "${dir}/${fname}.crt" } diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.crt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjBE +MSEwHwYDVQQDDBhhZ2VudC1lbWFpbDEuZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDIKXR0WvZcBrJPE8JFaKB8/CMi0jUshP3f38O4YcBh0nF8AcAwwC9/iLFe +xXHY27lRO1V0XpvebRzlFDMSoeqS5wGYge7ola9t5nR0njwWAfsAIe/dt9ZdJuxn +mk56IQV/cQXw8h/qHSZP2/utmHycWRkHY5U4VcAZX0IQwLBqk6+/ywxtl9MvIOuY +8yImz7BNwnm2ZfWhHm3EHqF7hOhPNnaOq0R4y0TCd+gUfdSF5t1KIQ7dLyrw2Bvd +c472rGpM89oQRuw6YvYe8/K4XLbtyZs3FwAOUW5ijJwUIRQMPVuJj60TQHcH9mIm +byL+ZCOI9DQgr3Swx05wMNddXSjpAgMBAAGjgZowgZcwWwYDVR0jBFQwUqFNpEsw +STEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMx +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/BAIwADALBgNV +HQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3 +DQEBCwUAA4IBAQA4q8Vg2LMKmvDy+Q+lBIZh6rbcTafMu//H9+AhPcGh1gR0J0Wj +SDps/woYSle04b7MvwC6+yPCiT79nIpZBQ5jHIcWxnE9tQa7DcPuTyPyalNUlzuE +1Pp2cYkFiYMv2lGU4z13y8PynoL+vqoij0LPXa6SP4lhmbvC/qWkf8IF6C6kGTWB +ZQTTOa0/MWirGlKDsUh4UygzpRICjO4ouzBKDTEmfLEZKpWiCY3WtrCWO6zK6OOa +O85k5MnBXjV/Z2Yv9MRABZDjQaFtJ6zTktlakjOyqoPMaPu5aV7B2T1fN4myM1NB +m+vtQqWUNYDOpTGk7QML5Bfz7LH/c5NohQbq +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICiTCCAXECAQAwRDEhMB8GA1UEAwwYYWdlbnQtZW1haWwxLmV4YW1wbGUub3Jn +MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAyCl0dFr2XAayTxPCRWigfPwjItI1LIT939/DuGHA +YdJxfAHAMMAvf4ixXsVx2Nu5UTtVdF6b3m0c5RQzEqHqkucBmIHu6JWvbeZ0dJ48 +FgH7ACHv3bfWXSbsZ5pOeiEFf3EF8PIf6h0mT9v7rZh8nFkZB2OVOFXAGV9CEMCw +apOvv8sMbZfTLyDrmPMiJs+wTcJ5tmX1oR5txB6he4ToTzZ2jqtEeMtEwnfoFH3U +hebdSiEO3S8q8Ngb3XOO9qxqTPPaEEbsOmL2HvPyuFy27cmbNxcADlFuYoycFCEU +DD1biY+tE0B3B/ZiJm8i/mQjiPQ0IK90sMdOcDDXXV0o6QIDAQABoAAwDQYJKoZI +hvcNAQELBQADggEBABdsPmfrkx45UDPmHZlsWwfLoY5HTFSC0TevLfPwraUXpg19 +tj9cN3REv0meCi7OvsC9Gqf6HfisoyeBAdXR6DYIwTo+aWE/p8RR/cvR20eJlIE3 +qcWlK1H0yylYKYhOzgNxWyXiPOsZcMt1o4WH5YREiDWza1VLV1lOTwk/lYYAeq0s +juEkh9RZq1NOZsD2GuoLPAAs8I1lqQayVBXiJe9JT0w1+2t6xXNQVNYlitlV9DDz +5DMRqflqIKcnQT7WV9su/F0JhoQuMB/gw4ScAnRf1djP6TrIu9NeE8i943AdNn6L +De8Isu+F/rGxCoObvCFOmudBQQ4oBWUe/CtedT8= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.key 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email1.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDIKXR0WvZcBrJP +E8JFaKB8/CMi0jUshP3f38O4YcBh0nF8AcAwwC9/iLFexXHY27lRO1V0XpvebRzl +FDMSoeqS5wGYge7ola9t5nR0njwWAfsAIe/dt9ZdJuxnmk56IQV/cQXw8h/qHSZP +2/utmHycWRkHY5U4VcAZX0IQwLBqk6+/ywxtl9MvIOuY8yImz7BNwnm2ZfWhHm3E +HqF7hOhPNnaOq0R4y0TCd+gUfdSF5t1KIQ7dLyrw2Bvdc472rGpM89oQRuw6YvYe +8/K4XLbtyZs3FwAOUW5ijJwUIRQMPVuJj60TQHcH9mImbyL+ZCOI9DQgr3Swx05w +MNddXSjpAgMBAAECggEAAaoels0e2VsQ5olyhyM8mm16WVLIMGuWtOLmfMULVYTr +fq2BDKa8fmIDn5ciwDvriZpltocG4s2jfOAzIIeKLNiQ8dS6yOJdyBsvgrzCISi5 +DqI5nW1m8fVd5+qnlmpp4kptUzigPVEqvWVF7YakGqS/CYmxdvt3kn5xTTRdAzXd +aWSE3T8rrvoXml7mx7h0VuYvy0g1VGvYf5tsEONsNuMkFyqn/cJd4S9MRxBjoqwB +iN/5ertVS83rSfvivbOtPCp75RVOTrMZqbFGda1bPfA/GA+ZQrPXYAAbDAWhzdcs +rDCyvlZ5SQ1v9WCZgKqmfc7BwxvGtah57BYwugZNAQKBgQD9Pl+qtFDOVbCIBHbL +YogcU1UoOoBm6G7cOqcVrxNXXdFTkEnp978Z6ZNdMzLLBhnGERiL9ebQyUHvkk9O +NRYJYpj0qlzzp4C+uGr7OVHTKMgEBXXbt2GnGNHqshNZZ4Rn3mte5cHeX9bxbd9O +2EIhjJmPFimbvdpTv7ku8LYEoQKBgQDKVy1B5HnIL4EG06lclLopp4IhWhNNmop/ +eYicF9b6fEREMxvClnyZ6bgr01ueX479caF5pZvzOGVHHQbKHAxPI8shueZ/2hcg +AvuCFmFbaS4y/4Nl9affyb0E7x8wyse98tXkeQSlF5PtSPmSu2736AoN2luoXP8G +o0Mi6+93SQKBgQCAHIfFEcc3Or49cRSvuz7kJXwVB9NhPACpRLq2C4WbUwjMX1gl +FoIEl9dG9NmjP4jWXFuwokuMH+RNUBTymYR2Zvy9+VlgocJNuXnVcZ/lVOIqtqBy +8IhkQfgf5MpJFkczTJ/AuDalSpKwPEcV2e1Kj5g3fJy7/wVuDLWf+4H5QQKBgQCP +BmI6QnsG0vVcUv9TLtoMmZg1ZQth/oxJoSm8VGmkc3R51LqZqG/5B6kkaWlIoHld +kxW1/CcpHEMSqmTjNkJJKp7qfxJ8azePbCxF/vXgfI+ogs5pouSAHoTa2P/uKFha +Xy1sK1LqM6lfwAaR/qVo3P9GXYQOlXW7RmotZCLH6QKBgQDfy1V0wSFjTJFOIW1h +K9VoeT5HOf/lbnohVpxjEeKZLybV6EvXdxYDIgFOiBLN5SFeiyg6SyZHQBDAUGO4 +vYrSPyaAdiX4GFWhzf3lfqrlm8krVfJ87KKoMg334aMdf42qRWBbFlZ3Q1oK1QSK +8vhrTD92eBqd2+9m4Kfs8FBSAg== +-----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.crt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIBBjANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjBE +MSEwHwYDVQQDDBhhZ2VudC1lbWFpbDIuZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCl93N93Sxm3Odr8PUOV7iQDDFy3399SkTSq6jGV1P9KIh2hszlSoLKYhKA +PVlIlYvCcbVflsixHDcyd/8SywstNndhdIhM1mpi6QBhEtnOoFjfYTfbVKSC6PUl +PoUIVqwumiB2VLiHCy5AHij1Ak7EMD/4gpvvVfGoG3j9jLNl+csu79j2yQYiDk9a +wQkoI4dO4jBAdq983Hz3lfKFj1qgrwUMSpMsN3KMi/6osJTzdOFFe8UuizHQFrnh +4Y9/26zZGdgOd31kmb0Vka1c3+TIwr9JbiQqWo/ZYSX6j187aRpPCl6tvUqVPSO1 +TPSCKHrkVauR8ZsuwgOGo+mR31VLAgMBAAGjgZowgZcwWwYDVR0jBFQwUqFNpEsw +STEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMx +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/BAIwADALBgNV +HQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3 +DQEBCwUAA4IBAQBeSAbhGhWQCclBrX8DYvQa+HvZIcsw18UILA2zuGvNoUgbWjob +3w8nMYSjQ6YGJns/H8jHOTShut8wsrKCRuEzI80LzYUF6MgpEDrxwlxbKQLBHg3K +XnCNSEwDYVJOllIUEAK0TByPP1YFHtd2IdZ9kTticVJdmravZFM18SbLt4J6X/t/ +EBuiJE27fdMqJSv9OP6PUg+MYakvBz3EGwzbjwn7NAB8OB+PPXd14Er2P8B1C8of +Hubiv5Hk3NX5QbXGkoetjzRLkpXCaOZNkWPcWxkPMv7cVozpDtm7E1UC2i7BUdPh +FAPv+O7WxDixLb9O5uuE4jnszg4Jhp7JfOqU +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICiTCCAXECAQAwRDEhMB8GA1UEAwwYYWdlbnQtZW1haWwyLmV4YW1wbGUub3Jn +MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEApfdzfd0sZtzna/D1Dle4kAwxct9/fUpE0quoxldT +/SiIdobM5UqCymISgD1ZSJWLwnG1X5bIsRw3Mnf/EssLLTZ3YXSITNZqYukAYRLZ +zqBY32E321Skguj1JT6FCFasLpogdlS4hwsuQB4o9QJOxDA/+IKb71XxqBt4/Yyz +ZfnLLu/Y9skGIg5PWsEJKCOHTuIwQHavfNx895XyhY9aoK8FDEqTLDdyjIv+qLCU +83ThRXvFLosx0Ba54eGPf9us2RnYDnd9ZJm9FZGtXN/kyMK/SW4kKlqP2WEl+o9f +O2kaTwperb1KlT0jtUz0gih65FWrkfGbLsIDhqPpkd9VSwIDAQABoAAwDQYJKoZI +hvcNAQELBQADggEBAKR6UVOIL9SHpM7amIzCKmZARbWMuXN3FOfpl2Jo/5VHrrqy +M5T2R45HJKD4tsNul/amcZ+9A878HAlXIAxM5DzyOXS5xJRiGYtcUhhTuVxDXesS +Qlx4JAbaObhhgcoqYMlMqq8jPJtNNainEcrpGRH/yfwwJ+EwTccOYvYmPJavQWQs +BYP1Xb8R0hLIByZpldQimjegpfurolvzonupdjn+HPWMENR/jgqUDgqB6Mke7hkz +wbIM3Fa+lyvMvGH2NRNWNsD7h9aZll8UJD0zg28dGBaj3CLf4cVOVM6lOnizc6Wk +yvGlHCV0tVcXoWMX11PLn4RtLEQ3jvFwymv1uCw= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.key 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email2.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCl93N93Sxm3Odr +8PUOV7iQDDFy3399SkTSq6jGV1P9KIh2hszlSoLKYhKAPVlIlYvCcbVflsixHDcy +d/8SywstNndhdIhM1mpi6QBhEtnOoFjfYTfbVKSC6PUlPoUIVqwumiB2VLiHCy5A +Hij1Ak7EMD/4gpvvVfGoG3j9jLNl+csu79j2yQYiDk9awQkoI4dO4jBAdq983Hz3 +lfKFj1qgrwUMSpMsN3KMi/6osJTzdOFFe8UuizHQFrnh4Y9/26zZGdgOd31kmb0V +ka1c3+TIwr9JbiQqWo/ZYSX6j187aRpPCl6tvUqVPSO1TPSCKHrkVauR8ZsuwgOG +o+mR31VLAgMBAAECggEADRuWC4LEVvtra6MEN7XF5dqQrTnpn8OV2+2izsdm4r+o +x3OuCw3vdnx4aZhuzMTC3QCOCpamExbfZSUFCD7xCwkaNKA3g5oLnPQ6C3CpsyAG +5N2Qw8nTlGNBuUdOynHN94BEwBPA3ymK5ZV8Np2QCv6kjsQB/M0U4OoKjDvoj00i +lvcUJeKvPcpae8fxuCmjfUUkxjquZh7ussUTREA19GUpD0SzGlFs9o4XRVTS63ma +VYENqXXRGH+sBcAtJ5bQswQpyxM1yZ3SvMeuvHD6z5VRV9kM3IvmUsVQjUBf9M1F +BECbZYtyFp597/0tqoxeQxcbGPlmtR+MaI3IBrW7QQKBgQDbpoHBLVU9nQgHsGAW +whGoEHdni4dK6V+F7de/oQck2mydZX2/5ozAONdKnmIVhUI59m8/cR89F1ruyYY5 +Rt8Znm+gVuQnZJzp4JziwAZK2oOlbxT1FbaaD+bIO84mNzhWB+lH/Gmp53rs57Nj +NV7OcaoD7id5jddRm27C0irU3wKBgQDBbqD7DZDPOPoAJCAuI9OSJEV9XLok1U0+ +91r7bthsJkDytY0BN/1tex8/Jc0ZulbEofj6VsVAYNs5ODLVpAR5a3zfhRPjBClS +FsPzKbkxcf+/YldeSNThrGn6szwE7WDy5FQl0dQ9Q0tJeZAv+mMnh2ArylfMPICU +PQDk2FkBFQKBgQC38yE1i+aYO/tunQpL5Olui6PEBrkeRoWmHwMRTCU0eux9gHKo +lMcJcFD0/+F6zmghLL4MLouP2RBhbEpJi43p4aLwb/SQ/RTgVShuSYy7gXbAxF+T +xURwj6KhzFwf6xz2B5gZnk1Laj2rS3wTOK4WTGCq+/b5OI27nWVMKUCPlwKBgQCT +TyL9zoj3p1aNAPYY7WPUqhbZr+J65o6bY1lAnlvnkQJpVGX8BZ3U4K0dNDaMX2Jx +AUFRtP8nVhvd6bSdCvn1ViyVNQ2+F484WuoDp0TIKR8xqz0KKsEk1F8pc5JD1lu8 +3rJO0wulVDhjzlZGk5MrqWdOMhzHB1SMkCC8Jx8xOQKBgQCwiiGdXo8OjhOBOP7i +0lDfH1vjY9XiVyyOLgLnyd+LcQZs0c1B3vc+fX29WGJMLENLeGfuRPD+tE3dMYTV +OV0BZ1+EM07s6zeVBdfNE7hPqTUY6Cmn30ajm0ECFDP7H9w3RvAdTdVJ8pAhyTgy +KF1hWgqSdhKqd+epUTWV8kgBDw== +-----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.crt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIBBzANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl +cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjBE +MSEwHwYDVQQDDBhhZ2VudC1lbWFpbDMuZXhhbXBsZS5vcmcxHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCqcD47G2JXhzySuwJX+2jkDW1CFe7WrmWtTaeneszgSz2juuFA39KGS2lC +/RBU6okPwhYoaCFLZYTeGx7NNaUYc+GJf//IMerzqbXTLsjh96686+7Iwqjn+B5O +Z3XfCF2quEC1g/uT+/ziqN1YmRMzMGXMlSQGp17sCP0EqW4Xcry9fLhCV9Z9kJyJ +fxqIG8fmjCyhUwL3x8iLmaRVFovhfLvyNPLLYkOcpDdeBuASn/7/rtV+bWfVWd/Y +a5wNqvIbtlp/FUt7VZ35B66SXp3yF1kaVl159BbkL2xzThch+0rRUH6OD3UyOv8Y +Ubmkl2Q7ykNJNh2nKwtNIqPt5PZ3AgMBAAGjgZowgZcwWwYDVR0jBFQwUqFNpEsw +STEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMx +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/BAIwADALBgNV +HQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3 +DQEBCwUAA4IBAQCMrpNT6pqqkKn+usB8sFWIcASP3OTnaEQVCpnoCoudrB4UHDZO +oQzzY3Ys5fcTK7rnXS7J1daS6Qi1g/XSiTCB8xVWXjz8uQtXGQXjVWNpxI9clFyo +IJBZ7Cm9h8QatFIF5G+e5E8G7HEe4Cx88ESAAuQ2cnJsARRB7DyEuegPqZunGc1M +T6Z5Y4Xn45zorp+TLLjE5zbrInrNeaVzcqFQYpMbzvFoWwba8G/1YobKggNIgLk5 +rlqUKrZKawRjtMHBnIJf74RLBZ8qQezycIUso8vTi3is1BdRgSfPEdlWAxIwyOoj +uSnhq4DT4QfDZK9u1J9e7pxthhQdEW9Eo6/v +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICiTCCAXECAQAwRDEhMB8GA1UEAwwYYWdlbnQtZW1haWwzLmV4YW1wbGUub3Jn +MR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAqnA+OxtiV4c8krsCV/to5A1tQhXu1q5lrU2np3rM +4Es9o7rhQN/ShktpQv0QVOqJD8IWKGghS2WE3hsezTWlGHPhiX//yDHq86m10y7I +4feuvOvuyMKo5/geTmd13whdqrhAtYP7k/v84qjdWJkTMzBlzJUkBqde7Aj9BKlu +F3K8vXy4QlfWfZCciX8aiBvH5owsoVMC98fIi5mkVRaL4Xy78jTyy2JDnKQ3Xgbg +Ep/+/67Vfm1n1Vnf2GucDaryG7ZafxVLe1Wd+Qeukl6d8hdZGlZdefQW5C9sc04X +IftK0VB+jg91Mjr/GFG5pJdkO8pDSTYdpysLTSKj7eT2dwIDAQABoAAwDQYJKoZI +hvcNAQELBQADggEBAHsn0ZZ3xnuaSGhRhjNFexlCKI/UKSLiAcDQQzegBjAIP8xG +vOxaUx+VE6q6m1/H4naZkB47z/agFK3ne7lCtlugJOiSmONgRWyE5S2kF+K+76S+ +5hl3G3zrdOWkTteSd9XTEoLJ78dLdkneSWXgpvxbgGpMUb1CJcsacpXymBXGXgwK +oOiOntggQwCOyksOcSNY1C/Q5nXe9eV+YS7C5RR9rsa8Lr1niuBMgsCS5YDOJLY5 +495fJzohhykfIrppWrH/pGCkT7/I1fpJtTO+DetgSZZyOILxls/c5Qrjv7J/CZVK +V0BkcpMElGGSJZwJHWNtsIHMqic2NvBtxifhW3w= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.key 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent-email3.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqcD47G2JXhzyS +uwJX+2jkDW1CFe7WrmWtTaeneszgSz2juuFA39KGS2lC/RBU6okPwhYoaCFLZYTe +Gx7NNaUYc+GJf//IMerzqbXTLsjh96686+7Iwqjn+B5OZ3XfCF2quEC1g/uT+/zi +qN1YmRMzMGXMlSQGp17sCP0EqW4Xcry9fLhCV9Z9kJyJfxqIG8fmjCyhUwL3x8iL +maRVFovhfLvyNPLLYkOcpDdeBuASn/7/rtV+bWfVWd/Ya5wNqvIbtlp/FUt7VZ35 +B66SXp3yF1kaVl159BbkL2xzThch+0rRUH6OD3UyOv8YUbmkl2Q7ykNJNh2nKwtN +IqPt5PZ3AgMBAAECggEARg8LVYqFGwuI95uNdBLw48UtIxwW6AIZ42nnP223536m +ILZBv0QBqpg29YVGgFFD88lJCGT9QpvBSPcG6z1g1rNTgwytXGJAfNZG2j8B7fi8 +yAWHStjRS0DvXDUk+3/Z2MISP0q4T/Ul1xmomrGZddIwFi879sw3tXhgYNkwWsxv +yaTKnh8OPSCE5phgi9WRbd4ZOzEu58Kb8Bowi7flN/M7rj8WN/jPBd39IUmYv882 +dEx2ws+iliqKCrMn4Us6RsNDJf100MMeJ+l7M32/e6eB/JuVNpU9ucdvJIQXJCJS +cbC6uvjAE5hfKD7HmxjrlcT165yKfperlByVAVP9cQKBgQDiMNMXrCds7LfpMZ7L +Ie/GuVyO7kbJ8Jw9kenwPhSvYui67hxpSmIEXvV2hMQtKbA8E0qbrmK5DOO6x7DP +C8QtxP5YuYZzu3fhT9T3aVHJ0gWBaW2VBYIu10hgudG+NxZiJX0HF1YBWcTzqkts +kkC4wpADkYnT+4+EmC4ImKCZDwKBgQDA5nXB+KvaLvzGA/ZnNiSUjbsgvgnpoaK7 +VD+NkNclaXye5ylcyE+T5XUF3bEHHJhQEN1XItsu51q6D1JFuBm50a6NZWud/3rD +tjrn65Xj0gDHVdsqwHbYVRNvUOJw2AbDLCSKtcgMcM7j3PZxKj4z5p7NdUMR8ZrN +ZsESN0G8GQKBgC2j/HRCYJIyEcoUIR6L8n3+2RZeLhqBWoVx8+puHG5nXt6ZPVJh +l/WZ053enFcgmlcE7YEMZw8/8pO1x49/qE4z+b6dcOvZYA8utKJXthErN8EtLNdt +DR+2gPxDgncHVpTnS5sqskgDfSJNsnpt6HFfkP+nVlBX6lWu/1rssTsxAoGAT2RK +oQvUxxbBqEnnNvF8urteggpVEBgbXtg80+oB8n7o8ImZZ3t17RvqPwDQJGorcgCO +6JcK+NH1cFBv/wvXYrgfDEjqsvt8LmKo+dznMv2ynlgCvSS7hSv2tMDogaMXnWpk +m8ZXUG95ZyUBNPA666eGQm95rD/xA4q/+dxOLLkCgYEAkkGgQQftl67cw14iqW06 +T6dOkt38TTalM7ctGJmvMIdBf4heJjhTBY6FChkgTBRP9h2VClXHCDHpbr2+vWYh +lGD7QQltNp2IH4VRLe1DP35DOqF9ZeDPML+C81TqyS63+Ev9RJOb02vaoxwzuRli +5ZGbmT45XuebdLvyVX7BPGI= +-----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MSMwIQYDVQQDExpJbnRl +MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt -cGxlLm9yZzEZMBcGA1UEChMQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECxMRU2Vy -dmVyIE9wZXJhdGlvbnMwHhcNMTQwNDA4MDEyNTM3WhcNMzQwNDAzMDEyNTM3WjAd +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAd MRswGQYDVQQDDBJhZ2VudDEuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDAjIjCGe+/hW62yuI9i4bVqZ3X60tQtUTvB1TA4TIAHSqb -MGSSuMBdPKSkLRrASzCUUdPBbNo1MDwcQ6ZulyE6emIxp3V+ISrkRLIfYw+CSqCu -MjuaXHLtC03qOcXvFXYMwJ9xGHkUnylKe2IMVafbtgQ6MXF7E9VxZu4eddf9OCAX -I5ZgehuZjiOOAVZaMfE/0xIUc/aQcyCstv8NKbFdET32nMYnuYprkY12LVsnDsdi -Tfa2zdqHCJBXmAb23QvwRp2scFy9QcCbhF063Fklx4Ix0ut1ifjjywiwDKu/OMiM -2BwpU+JnzSHhKH2VIdJbMpaAt/whyOJ9RrKK6ZDFAgMBAAGjgZowgZcwWwYDVR0j +A4IBDwAwggEKAoIBAQDa8oNyLxOwgoJa0thHlB3CPLAm2mcrStPD0RHmHxlRihsa +3BrGdvOekD79eBi+7RmIsnRpUL0F+7oCS7skQ4z+TkS3hgoXyOgTgK8UWY3osNny +B/Y/61KIzCIlZXXey2sQYx6jsaxxsyfrs/ErdFFYbEE1MLvRJadSw3MTcML8WhCH +S0jB2DVp9wmKDGWtWHBRFpvkxwGt2u4Zlux9iVOz4XPNxxh19W+vFBms9DQDmJmN +c3X0ZefKlrilKdXMg2BkFEIawMRVJgfNvb5uoAjik6KQdFG3Vnt9VXMwDmO5DmXT +wElOKAwqouGeF9qp3t6DPLOylyIYjeC0uPbCy6ETAgMBAAGjgZowgZcwWwYDVR0j BFQwUqFNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9w ZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/ BAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC -MA0GCSqGSIb3DQEBBQUAA4IBAQDAgTSbvhE8//g0WNmT3kXVPuGg02v9ML3Uai1f -whq2dbBaERWhfIHBZw0KiNw0fI0MgKtR8qu3nEFOrXZEIgVCtBDv/aTGoudT5nil -WF99bC4OkoAiYLwFOVLjXbc3+wOB7dd9G6pdBnETmWT/MO3WwXxexNx/7yxY2UDI -4g4LOkxQMWC6zCEHp7lNofEKTFGlFgaP65PeLZ2GxnvAVfm1k7rMSy7eeQV2L1Hq -40LhAbQSqsSY8hBoypQnCmf0yijFwTH6wP6hRCu0ptu//W9BSJ4L8bTNg7n/Ff97 -QXtq2qn4OgLIoCSjshs3JbFHiPEpC8He3JKXM+gHWqySe8Ln +MA0GCSqGSIb3DQEBCwUAA4IBAQAmSiSyp5nlk1Cwn3Rw23bqCIQ/WlZzJE/Uh1+Z +TuiwVbZknaJ0gsZELmkFGsvX56OS0nTPUyl6m3Ct5vCAX33nhOH94njX5yFte1sO +zT3AkJZqz4elf5Li+aGvgLplN04XH5snnw23c2cwhbTu3GwZok07MpUp09jonNfK +RfV295tTfC/L7+XXz9uOPwI/oCNiZNTbpl+pshXaG9t2vtDiIgQUWF4jXzWpUu9I +wFo48IM1OVQrujb3LliD4fLZItfJK/xJKIKixTxrPy0Xu5zdz7DyTBJnvZYiUVtU +8CKZR+5wtc8aKdDxfNaY7nORiZEyd/uprVb6okCpZnVxxXum -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSYWdlbnQxLmV4YW1wbGUub3JnMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2vKDci8TsIKCWtLYR5QdwjywJtpn +K0rTw9ER5h8ZUYobGtwaxnbznpA+/XgYvu0ZiLJ0aVC9Bfu6Aku7JEOM/k5Et4YK +F8joE4CvFFmN6LDZ8gf2P+tSiMwiJWV13strEGMeo7GscbMn67PxK3RRWGxBNTC7 +0SWnUsNzE3DC/FoQh0tIwdg1afcJigxlrVhwURab5McBrdruGZbsfYlTs+FzzccY +dfVvrxQZrPQ0A5iZjXN19GXnypa4pSnVzINgZBRCGsDEVSYHzb2+bqAI4pOikHRR +t1Z7fVVzMA5juQ5l08BJTigMKqLhnhfaqd7egzyzspciGI3gtLj2wsuhEwIDAQAB +oAAwDQYJKoZIhvcNAQELBQADggEBAHb7MMkCcYBMjtVscja7jHrRMPRJmiqL8jmh +Y0JRrS3u7zUK/8IDkR5lbh1O3/Ou5UKCWdrkC0aaqv/xhP+oFcTCi8bj59nsBZin +35tkfnWlGuf5T5bbp7P+/7XzynUciEULCGwvLVS9V8xDY5z5FvHx9IFyzkPMQXJN +uuucShja9jVTqN2x94jkjXSX/dRPKnHdE2p3jExgRddwGYNGvCgoxK8oHN7wspT2 +Sze5Lxt6wIW8xbZk5yhJUdNYdyYiK0FZ2eIvE6LLA1ZI+VfFb4O1lxR6g1OQ93v4 +0LQ40jPU5JlDaPAalJzIv+/9SRB9OEavo61TZSD6dRsiMumG0r0= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAjIjCGe+/hW62 -yuI9i4bVqZ3X60tQtUTvB1TA4TIAHSqbMGSSuMBdPKSkLRrASzCUUdPBbNo1MDwc -Q6ZulyE6emIxp3V+ISrkRLIfYw+CSqCuMjuaXHLtC03qOcXvFXYMwJ9xGHkUnylK -e2IMVafbtgQ6MXF7E9VxZu4eddf9OCAXI5ZgehuZjiOOAVZaMfE/0xIUc/aQcyCs -tv8NKbFdET32nMYnuYprkY12LVsnDsdiTfa2zdqHCJBXmAb23QvwRp2scFy9QcCb -hF063Fklx4Ix0ut1ifjjywiwDKu/OMiM2BwpU+JnzSHhKH2VIdJbMpaAt/whyOJ9 -RrKK6ZDFAgMBAAECggEAN5EW8nULtEisj8HzEnHOoqCFdcdof80gLJ7//X2/sTq5 -jfmkPJdmpEGY/ewqWS4ZwePvaVWhj1HxNvLq9+VTaI3jApNiG9k0iJWuldI/qokg -38SNYNmnLPNjM+IiVgHqhHmeScQXRTU9dEoxHIAnpgdcJvMX3b32jfh3ZYD6qQ9m -EINbT/x+L0nRFLj6MzJyvwydlxP38koTLyLcPKEZFpVQkIVn74kEzGEUl4PzzUnf -28l+IbXYN0fZwsfq05QFiscx85rGXE7IkhDkF/gkJxrQVDJqjIOW0v0KzRFv075b -cuEvnAirJB1Un5OdFfTNMobjw/r9XDdC/A8ZeXnKrQKBgQDrGxwpn7Nkwx1nHIY0 -WiAwDhnSzq3DOzxMC4YWMMIHeEi4DC/HzS77OmCEjtL0UgtlnjrKTW9hK1QE/DQk -FkvPOyD7/47X2p2T9oIC6yZMKfN6KldHIhQyRR7Hb7/ke8Qd+ddXOyHza2GYsZsK -/AXAKAL4pgEiX9m7neZm/zgN1wKBgQDRqTZNGh0+0eRyju8No+jnoPflJIle9G8U -EYXeMC+oKlnk5c98hThFgxxrnHriC0FHS3QvR0Xk8B92Rf6hmf6Tm/lmkVpknXxT -uVxPAs/Vog9oDROfrymNTU/Sm/JZGCOcteOsq+ux0kAFs5pu09Qx1A5h8n/ui6lK -pJpo9uRqwwKBgQDFLh6HlmZJqw7c7OJtw+zdAOaNlEPNyr9+c+fIcqo4w5wRqD7m -juLJV6OdaX/p42mll3htNValo0Y7TB6a6Yw5SLkYQLTXuoWQlB1kPz4GgOSwYBSF -F0LjCm+PCt1gzqCkF2eQxIpr8nKPMt3673YPPD+JtQicgNFG9l5HoeOsTwKBgG9I -EhNQdg9e1pNbfFTQGBHBZXJRNzyYHtEXD+fDybfHatlMhRmBmEGE+rO/ZZXPBYHy -8aMagGWocfqT4jgiBXaRhgnDwqe+0zuZGf6x7mBk2SqQLkdGcJaYX3LY5QQXBOfr -vwFqavqCLwsXrjN+6ZTdChlA3lhd9qgwv+hQiyKDAoGAc3ceSPFNFEfXjcAltARk -uJxH6tgDGwRaxvAgkqNy5yW4PVUYttipVA6lUlSI/BoCsRaKA10tmX9953VLtOQn -RT0ffDpaiUcea+ZcfboFRAl1qeW9nqY+o5duS/ZusoG17K30Fjaulh2mWV79+kjP -+u0OWrAQ82oICBolbMGIPz0= +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDa8oNyLxOwgoJa +0thHlB3CPLAm2mcrStPD0RHmHxlRihsa3BrGdvOekD79eBi+7RmIsnRpUL0F+7oC +S7skQ4z+TkS3hgoXyOgTgK8UWY3osNnyB/Y/61KIzCIlZXXey2sQYx6jsaxxsyfr +s/ErdFFYbEE1MLvRJadSw3MTcML8WhCHS0jB2DVp9wmKDGWtWHBRFpvkxwGt2u4Z +lux9iVOz4XPNxxh19W+vFBms9DQDmJmNc3X0ZefKlrilKdXMg2BkFEIawMRVJgfN +vb5uoAjik6KQdFG3Vnt9VXMwDmO5DmXTwElOKAwqouGeF9qp3t6DPLOylyIYjeC0 +uPbCy6ETAgMBAAECggEATiG6zJWGJjf8gqK5MZnmoLj9d2/63xUdysQh6gfxBgsN +ryEaoPYz68K358XitsdvlDtylV6NhcQV1dnml4NPnjSaCw/XLDOytbr3P4DRxlbN +/7PdqWO1mUGXcdbqIC5hL4DfnzS5P/3Z4h/dkU859B71iiCw9WrFciBzQV29B0YH +n29XP9xHistbBZ1Kygp9uAF2VZPKr/b9ltCHc/h2fC+OuIFPem2sOs4xkUbRY2Ob +F77E6Q8i5xhZNcsLwp39r9pOUyMDoaGRsZrcBynxL0FIk9gr6goUu1SmRdOAuX8l +noORneVcxR7e22gd9eZSbaNruu7pCs1hGzx6/LyYaQKBgQDwoJkwWH9/FpKV5kbD +5AHtXZHvHU5XoivqisSWdLJNKJCXxP4D+v20ziszwlI+cS7QtfVTv9JLbAnW5nFl +eIIJYjbTOfZKKvXoffUzAOiA+9XVJhDN6TGX0ylUx6bdkR+IYMCO/bqTJVPyrt4Y +E1460uYYIPp8jx+pZADgf9WOJwKBgQDo71f3pa0uFn/fxGNtwVrkhUxoT1iF7PjU +UOW62uCnPTEmNJePMNDt5NAcmU8IvI5tZJdmveLJFQ3vTky17ZhGQBKWD5JNt0hw ++tAAyHj9uYGh9+/9on2RQcB6SRQj8jtuzBlwaTMAY0OSRXt9A2okKljNyUym1OZ7 +yc+hQTQVNQKBgQDG+ubydZwoyc+5qUzHXFrDoGa3JrLMAMz0DIB4MlPnPi4jcsDj +JcB6rSpIiN5dyFaAPJd8A9mwiMyyKqmG0VomzFKt5PZnapMjCFWlMZESaC3xLqMi +wz1BYVqCQv8XpZQ/wFI7bFHGsE11F2mM2ydG2XNwbIT2T5xNBOc8vjkobQKBgQDG +e8wVddNPxlRINGwVBgGRYBRvfc0MsZSGNjjLM97iIr5Ss9XBafNeAu9irzAtXZ5v +kJuCLKOmfI5XL/0luMNceRRdUf90wYy02MONJ0YRzZSskzyof2FH+SMuED9dMfd6 +veefZcTTu8HETfif2d0D3CG3mKP8NMYBDUIjQhBDUQKBgQDuPGcH48BRhibhzY6M +8jmbAmn23+pfqb/Te3Nut1UpaNFOExSu7WDD1QWRjqHwysV4kMGQQaH9gwk7ydjk +O5Qu6NMxz2jzmJBJwXhiAFirrfNs6DSgdSFZ/9Xx2bICWFge80wUP4I0XCnyt/f7 +xCTCfGsaffgjGa0a0THnmjwS6Q== -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDsTCCApmgAwIBAgIBAzANBgkqhkiG9w0BAQUFADB+MSQwIgYDVQQDExtJbnRl +MIIDsTCCApmgAwIBAgIBAzANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh -bXBsZS5vcmcxGTAXBgNVBAoTEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsTEVNl -cnZlciBPcGVyYXRpb25zMB4XDTE0MDQwODAxMjUzOFoXDTM0MDQwMzAxMjUzOFow +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow HTEbMBkGA1UEAwwSYWdlbnQxLmV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA6EjKALrLYvsuUlBYm5+0Ig24R4axrhFPv3h8n9iL3/33 -Fl1tdxFGMi9jtRcNIajEo4XboD9tSvxWVJQHOOMdPh4Fo05ygcWn9k93W7xZmEiP -X+suzEi6LvfoaTsmWN/a8CVLRbfB/xsPLGUhCB7qOS0nmDcIp2A72nBXCMbXUnez -at5Hsmdb9DJPzuKuv5bgA2nL44uPDKXwG0n3AUVqyIab3uXN52q8nFGg62AVSFcy -8ergApXR6F6Nmy+HdzAJZeaxxN93pi6GrT9Bxh194iffeN1Dl2SVSWB4zGsjt1NN -+4z0Y6MhAt/1g+ZIOOitW/6U3T4KOsamGd6ZVsiGbwIDAQABo4GaMIGXMFsGA1Ud +AAOCAQ8AMIIBCgKCAQEA30VndhiWyA0M9LXkutDYYdN9W945RiHn6mjBrCaTyxYh +GunD7Ub2mjoivkgSQt3NlQRRI8bbgKGXYuAJD8tFiZtBYbh394mPHBS3j/cbkc4k +mbnO2v2nR9DUd2TgHJ/gpvSQij7ullKpOc59h36wNAtKtlLpacEEdd6rJBVwIDOy +oUlfO5lW/8Aw9K6nh/SuKLV7a1qIu/s4Lkv+8tQpzkgrnRPh1Nd0OJpoXNuBOfx4 +Nipj07yGV6PWtBWAvOkKXSC7ZxudmlUFc4UHPwLZsh49xjZJGSrspJETexknu9pA +FGqXAqE2kJhAWtehOiy7TKbIuOj1qKE+nEm1Ru44jwIDAQABo4GaMIGXMFsGA1Ud IwRUMFKhTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0ExGjAYBgNVBAsMEVNlcnZlciBP cGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExDggECMAwGA1UdEwEB /wQCMAAwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD -AjANBgkqhkiG9w0BAQUFAAOCAQEAJB2a8oabIxE0mn8tINQtUbjE1UPI2HU4HxnX -eAO225BogLCbo5RgUo+V1VZp8oksWggn7ztavcY/lpR/0lEiC/rrr2A10HCybAkZ -zBudei7Wi2ztBC8CTnO61K1/jvxHgUElufGuu+Aa7YlAJFby1ROKWJP7UhAS4uR0 -mMj4yqApXsp4+/Qio64MNCjj6MuCyUqcRoq+QM7m+/Po8i1DWesIJ0N8l68/uFiY -ByZ8Y9q3VPn49LUf/h+jTRus6PDoCzY7Zbh/L8R1g3p93ahLiwXJ422ubG9WJh90 -IAG3jJaGEfLCxPQ80Td2ZnOP3Xkf0lcK11NX7IuXeGNSoK7flQ== +AjANBgkqhkiG9w0BAQsFAAOCAQEAHwlUEuXe8jtnvdX49q3JVYs/wu+NsMg0+jHO +3JKWHz+2xjOYrWK1CgoAADAXE7/KH/7XPho9EqSlR0eANtUvHcOLuruYxrQgPX0T +nkyjLLEF4Bc5rh45XrDtZuvHbhtqhEKMRs9yjlVCCKBUJF4mVH3dNapSnZaXfSOh +VsmK0Ylt2iP11zbD4ncOnCCb4GKIqi0QW4rILdSHy8L3NxWCKP5QV7G0dQAYw0TX +syfA4NvG9SgGA/tR62B/+IO62ZYPTBdVceTWZDs1DolimM5g80DTl9kz3nLaPvNv +k5pnJWP5FV07PVgEf6I5xVok+l2wibewBBTAROa9I0FMfK/k8g== -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSYWdlbnQxLmV4YW1wbGUub3JnMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA30VndhiWyA0M9LXkutDYYdN9W945 +RiHn6mjBrCaTyxYhGunD7Ub2mjoivkgSQt3NlQRRI8bbgKGXYuAJD8tFiZtBYbh3 +94mPHBS3j/cbkc4kmbnO2v2nR9DUd2TgHJ/gpvSQij7ullKpOc59h36wNAtKtlLp +acEEdd6rJBVwIDOyoUlfO5lW/8Aw9K6nh/SuKLV7a1qIu/s4Lkv+8tQpzkgrnRPh +1Nd0OJpoXNuBOfx4Nipj07yGV6PWtBWAvOkKXSC7ZxudmlUFc4UHPwLZsh49xjZJ +GSrspJETexknu9pAFGqXAqE2kJhAWtehOiy7TKbIuOj1qKE+nEm1Ru44jwIDAQAB +oAAwDQYJKoZIhvcNAQELBQADggEBAHsE+gAgj/uSpCso9Q8kPIpAj4/+KKqK3gfm +3JSdet8/Cu7BCujHFO5QWmgufGpUb3vrdVu30QDK1l7qQynwh9FVdoyPab8d4FAx +/4kjh964VmFRjBSS4hflwgdaTJ/px1CcD0+l+HSJY7rrmM+r1AObIs/Nuon50o+P +3orjdir3lBacV35nXTnQ6MN4XCPj8Er/k+1KqAzERz+NxkAgrZsFIFdb3qzt4uPG +9iODdBahzUEE1bAkABXS3E5cguL2BfaV8NcGwxrEMZWwwgl0CW/jVjGkzS+StMgx +//LCjgsdpRIWLnUFuJQxUegGu/vHv4H5c3ewcVwXn87eto0s8ag= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent1.example.org.issued_by.master-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDoSMoAusti+y5S -UFibn7QiDbhHhrGuEU+/eHyf2Ivf/fcWXW13EUYyL2O1Fw0hqMSjhdugP21K/FZU -lAc44x0+HgWjTnKBxaf2T3dbvFmYSI9f6y7MSLou9+hpOyZY39rwJUtFt8H/Gw8s -ZSEIHuo5LSeYNwinYDvacFcIxtdSd7Nq3keyZ1v0Mk/O4q6/luADacvji48MpfAb -SfcBRWrIhpve5c3narycUaDrYBVIVzLx6uACldHoXo2bL4d3MAll5rHE33emLoat -P0HGHX3iJ9943UOXZJVJYHjMayO3U037jPRjoyEC3/WD5kg46K1b/pTdPgo6xqYZ -3plWyIZvAgMBAAECggEAW8xIaLlpn5h82fL+0aHv4nVhmoLUvWICDj1joxhFTyRi -/ee2VxPwley8jtipS+AStj00asQmQTgwNgS6RfUbnWzn9X4PKtIeau1E0nBBSDmE -z/d16y3ixqUaqFvD7+On9rgGY2sXs+XRvU3KHBd7oVUDDNjTBrK1dMJo4wMSHXiI -JOJGujhrzHcDwERsCRHR6lCg80bwP57420FgOt6iA+uEtAa/JS/RL1SNzc0/ZfpR -xoym+7uWsTBxdSHROcv9yObip62C8uPySNW/13kkXZnP5IFl6yWh5xVuQJaSRI4g -zyLc8p2JlVg8SSjpXvP3Y5cmOrPCqPUBQsDPIS3C4QKBgQD0n78CZSK3Pr7w+DPl -08QpYiFWdtaz9FC6syRuW3+CW59j3WqrqczwWFrP7Uq1ltZ/tc+gQJaCMyBPTxS1 -fSMrWK9cU6qVz+DNAKAq+KbRjPDG1shSRy4AaUj3BHwazjCz7JcfFf9zC3N+Arbl -AwtoP/mDv6dUoaVLxT0o9rFe0wKBgQDzFiOLZTxcOrN1ELiMI3jvhZ8rffIQNQEw -PrrKMmqRxnpoXRfJSKhKjddLX/I2vbPc7/luF0rDTjgWyNmj9bqOYFrr0H0Cukh0 -jnRh/UDxxY6TfX9kFTqameGa8iaYW7+CZyt70TFghnMNprGOi0AWqIIQWJjo8wAe -wE7eJikQdQKBgCVuPB/1sbYwV68vi2FjYeSjK067qGaov6GRv8VTItOjiWQSgTv7 -I2yzWFHwkTnv9qpb+4Ud3OnXQN5hz11l5HfBfjO2aae6wz8bFWb93Si7VCxYuRY9 -5gASHCQP/51qZ3Fghk8vYoMMAYQKjknEiX5OK7PJI6WDhnNtnsoh1UoZAoGBAOcm -gzA3WPlevG0yNDrWNFaXWm7x+W9d575nuBkzbdEmyhlZhJfbpmQWksZS+zEAnPsX -mrkMHwSu6XkJDqPxs69Vgr/AZk9ksidbmuHe/5WnxVcMvEmAQlTmxPghEYBJpkcr -MdcwamKeFuPBQhAI7C1fmSZQCT9iaxfSyGxIHz5NAoGBAKrPfTFX74+Po7Uo+qEc -81VMFJEwTwVJUX0+EIuskG6LIDAdRo2KWr/Rl8XvFcAj+BfSF7Y5eOA8IeqGSJHG -n1zyTmZjMQDyb6ECu0QiA8sshM3ICsrEySvTdGgw7pMySIen9Ku066bvdq+FOIY1 -zH44viWG+2efHCt5r2CehPU5 +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDfRWd2GJbIDQz0 +teS60Nhh031b3jlGIefqaMGsJpPLFiEa6cPtRvaaOiK+SBJC3c2VBFEjxtuAoZdi +4AkPy0WJm0FhuHf3iY8cFLeP9xuRziSZuc7a/adH0NR3ZOAcn+Cm9JCKPu6WUqk5 +zn2HfrA0C0q2UulpwQR13qskFXAgM7KhSV87mVb/wDD0rqeH9K4otXtrWoi7+zgu +S/7y1CnOSCudE+HU13Q4mmhc24E5/Hg2KmPTvIZXo9a0FYC86QpdILtnG52aVQVz +hQc/AtmyHj3GNkkZKuykkRN7GSe72kAUapcCoTaQmEBa16E6LLtMpsi46PWooT6c +SbVG7jiPAgMBAAECggEAGeGkvabaC7SflNpk+lbtTmHCvVyETSHvDVg5YyhRp5AY +JFOG18GLIpOspVwbac7mXujSMxCBL2cL8nU/96m7lNj/ekF0/qfehAXOxYyCkKNR +0NQeK3qN4qBNkM2LwSWzWAcvnibvZ1OLtFzAZm2EJXRNo9b2ocCzMKqwMg2eIs1D +7Y6jc6NshteDR3aznOSilDR1keC+upG5ovMHdKjKgiueQ22Cyin48Xz8wkXRVcYd +R4AdX425lj2YN7ZX4jFuX6TmPbUnU6D/p50V+FGc/mSpiwdP/5Ye+PXfBSwdYikm +YMYU+V2+i7dy7LwoS7ZP8jiORcSDjrUrpkscvFgIYQKBgQD0ibbBc10TBEoWnTOb +WDqERnxC6Ly5xpZcIEjxEwYUgAf/xo1nMgBKr0sJAud7vd5MI0B2/sLqBEIIAb+8 +Jp7c0x7fF6FF72buXH6//1AOTvhQO8kIzgC+tTuLcOZjtY2HdRhfqPo7++CYVdf3 +C1e7eiNutBYSZhD2tja07yQqEQKBgQDpvIDBoIKe0mH7PV2giVquwiRnPMA6Eobr +vCwER+wI32Gvhry5hs/p5mKJu+0Xy91eu/X+OySAuoYmziZ4YQDd4CmgpTmKfanP +OekCW4NN3SUNzLbyWsvf/WQoIp/cCqmSyFlm+78tNnc0f43EImktxIuf5YanK31u +kV3gUJ6YnwKBgQCWXCs0beP38nY/y29VAH4SoWh8l7CbpmEDypIvMVxWtJa2jfaa +UmNdiMVhiBifvJJ4TGCyCln9HJshYznqzfB2pZXg5R2z681lmRgSpEEsWyBR7UDi +mhBZMwSm68et3Y9lj/lpsHQU9/4UjQwbCi1qyGILCs5bh8i8ejPPMxAQkQKBgQCF +h2sHMutsUEQpg6U5UT69F6w0TkCC7JKnmZiL2Yq+ht8Hp7GpS5r7xOIGTZXlQlH1 +DOw7kQ4JfIWnJZ5UpxYXqzgczkdRFvkrqamz7xPUJ8JDX/AkBDr0e438X8tzqaO2 +dz6b1rYg3jy7gSM8KWxs+RvK/RUv62h9Ag7fVy9DgQKBgQCWXj/8MTmV12VRP5OI +Mg2jT2rH6AoLThg7ILOJtRUHFetXxiGsN9RTeu42budtNkZy1RQRB51xyJU76lyT +1TDBrFdeUL1OW1YgSgg7h2K/5mn618O6OqGpaFxXL9l0YRwNXWo10Lv8PF+otZ9G +ztAEC8EKtu0Fj1boLrLkQ66HBA== -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDsDCCApigAwIBAgIBAjANBgkqhkiG9w0BAQUFADB9MSMwIQYDVQQDExpJbnRl +MIIDsDCCApigAwIBAgIBAjANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt -cGxlLm9yZzEZMBcGA1UEChMQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECxMRU2Vy -dmVyIE9wZXJhdGlvbnMwHhcNMTQwNDA4MDEyNTM4WhcNMzQwNDAzMDEyNTM4WjAd +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAd MRswGQYDVQQDDBJhZ2VudDIuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDHweG4FqoG5dYD84YmGpGUKFIBy+ISXaZLnjD3JhOb6d5H -GFFFhBIuWeM8ieaY67crBpVdjKWuQAkWayWYdCa8npC7YxxHKphLbN81eL2WLraW -2N2kji3TwwA6J+QQVPAUCgPUQcC/ob0x9faor+zKrxQPTKQ6xtuSI+FIkWiw3+e2 -W54ONXx2QrKDwbeXXXNG09QOJVJxW3DueycbCH4sWa2n/ODySeC2t1hU6A5e6Z0b -UhMtaixdbQsKfsid2TpEC66ILW2MlCYwv4EdaOjC9FbizEXR4F7GAg2C5kPnQvhA -iRP6MAeFW5hFCkjOGBQXBCNxBSf9xPzmV4WGLjA1AgMBAAGjgZowgZcwWwYDVR0j +A4IBDwAwggEKAoIBAQDs8wg1+xrBWHcp9d4b3dbIqHQiuYtKbXlGU52lKoU3vArM +b2lu39EWMXrxg9Dn3joI3X8nUynGlB1mHvog6/RXggw2T5WtPIviAMIR3niQWS3g +YrgpT90GVeIPIiuVh76IUFnz+a9wfxZ3rsXRU74kXOzWXVZr2pSH1HjSpWmR6Hgj +HUPi7/CRPL/V+/nvyY66cvxqgtOchGYhZO3+0zeGnXzVlXwdleCQnJ72EAOIyvQh +rJhKiHvYw9/ZTD3VMHntBUu/QFB6a9ILKxggB3iOQXE2HwPBqO+cJfoi4qTpywE7 +MOdI0q6MK9pr6r8TAGAlEPYoImbUFHIehJmPvSZDAgMBAAGjgZowgZcwWwYDVR0j BFQwUqFNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9w ZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/ BAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC -MA0GCSqGSIb3DQEBBQUAA4IBAQAVDnNbC88i9mZeGy5KTbV4Xp0qZcPY5+w3MP6S -65DOxCSjw9ZIHF8E0EgGhG8bHidf7+z5YdyVald9lDV8PItjXzam+VUMmMuPdJIR -cwLdRjd30NmHaDQEjqsXMS+KaZDKUCIxaOcYHb4D2XU2LRXhmA0JMteQHLo0Ugua -2WoanhjV+7KMdY74h7aPc4j9KFEOzfRLFWQYpIR3ENucRLkPX4Aeb/qrDlQaSfw/ -BXma+8+4F/P4JjlE2wRNI8nSvflQGJ6fBnDgSyb07M5RqM/oDjMz58c/TEv5jzX6 -X4GizfT4l0mNtjxe3k7+tmlLsDaN2aiY3jCQ5D8kzAhdM+rL +MA0GCSqGSIb3DQEBCwUAA4IBAQBPtNXbJI3C0/8gHvB2g6tfSGBHVvOH1lRpQ8FY +EYW4m8EubV0cFdDpd02/gG4hOiCYFOOcMFENGOTRGSIMDWUED7doNttxHFWxVOEX +6M98cTlTpWW3PThCru5Oa4CGFcGFbo8Khb4vvZv5vIwf8ShUe2sl0I3REZXUx5ad +fQc4sXiiPdf/P1D9ppulz/UchMfg9sg0ZRjQmVmCBZvA4/P+rBS+T+nJQ0bfpTmV +EOVG149Lt+61kotJmxv5/J2UYRneWBmRE7P8d8DTqhOHQI5cHICgVq5Jw9m7nGlS +LB7F4lKYXXU/9Z5JVKtM7/m4+MfCHlQyocd0cL9laKseE2Ek -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSYWdlbnQyLmV4YW1wbGUub3JnMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7PMINfsawVh3KfXeG93WyKh0IrmL +Sm15RlOdpSqFN7wKzG9pbt/RFjF68YPQ5946CN1/J1MpxpQdZh76IOv0V4IMNk+V +rTyL4gDCEd54kFkt4GK4KU/dBlXiDyIrlYe+iFBZ8/mvcH8Wd67F0VO+JFzs1l1W +a9qUh9R40qVpkeh4Ix1D4u/wkTy/1fv578mOunL8aoLTnIRmIWTt/tM3hp181ZV8 +HZXgkJye9hADiMr0IayYSoh72MPf2Uw91TB57QVLv0BQemvSCysYIAd4jkFxNh8D +wajvnCX6IuKk6csBOzDnSNKujCvaa+q/EwBgJRD2KCJm1BRyHoSZj70mQwIDAQAB +oAAwDQYJKoZIhvcNAQELBQADggEBANxNVI7ySbxEI3azUWdlKSU9MIVLC+gXIQww +xYEU4zs3EhrwAI5TD+Ot/8vRSUO3LdSqSO/WiZTdxp2/MAUzyH/iLrcY287o/UAX +RVkqiWxyPG9KnUljz9crHnYaV0zwtMBOKtvNEN8gah1X+y/iWjgO0y8fZsWOVzAS +y1na8ZpVsvw1A61EuDrhwkprsyWJBCldeBBd0Epwaa77wHHk7IdHTepXE0bGH3pv +EVHPspQHOxk93MEs4yPAysXu9GTSqnMtjiZKlMJ7qz3zgnkLBLVphQH/gIsmPRp3 +BaEMFJM2LTZd3f26P/WG1VJDWE+s9kx2FBy6MTesAtmS2gj5n1Q= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent2.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDHweG4FqoG5dYD -84YmGpGUKFIBy+ISXaZLnjD3JhOb6d5HGFFFhBIuWeM8ieaY67crBpVdjKWuQAkW -ayWYdCa8npC7YxxHKphLbN81eL2WLraW2N2kji3TwwA6J+QQVPAUCgPUQcC/ob0x -9faor+zKrxQPTKQ6xtuSI+FIkWiw3+e2W54ONXx2QrKDwbeXXXNG09QOJVJxW3Du -eycbCH4sWa2n/ODySeC2t1hU6A5e6Z0bUhMtaixdbQsKfsid2TpEC66ILW2MlCYw -v4EdaOjC9FbizEXR4F7GAg2C5kPnQvhAiRP6MAeFW5hFCkjOGBQXBCNxBSf9xPzm -V4WGLjA1AgMBAAECggEBAImIQaJ/aD0rjThXOKuPa1/4is3V1CcU92Pk3I2tV57n -XDTS11HnZqUnGj8aKvxqfkhPVhN8vK3GRPt8dfwLLIh9G+UIjfWprAVyRhQIt29p -Zilh0uhzQ7Yi8ksYH/vmnw0NACnpw42tDBgT4umthnjeJg+KkyAsvgxWS8us4cm0 -h+wT6jMhKTGJKys83Py7CYIH4IiJ/1NHqkyXOdJjmx4cipys+3RYLeqnVpYhdKab -KV9B4WOmsb3NVQvtcQ1PDTohFeCZZgMurCLGx4/eGSvy26YyFKjiqFi+rqjQPiRB -YFlnePXE7EXlPzfimLGE+zYG7SMM89Z/ou3yevT8k9kCgYEA46DW2pzTHLno9qxL -AhhXrMkAoSluJXtekrOvQFqeYfCnPQPSfpN4D1i1UhFmizZ1+hJqjRaxf3boOTwd -hsy0pxljknoNtBNu0rIPmMNqO0wKmB0LapMqdsYbj1PWPP+MoyyVLj1AX1P6Y2Ee -vFU3FuBvKzpb9YQKFIWzicCoKv8CgYEA4Ke8tsj5ut76xD9bVrSdR4MpXWWgPnwa -lpsZ7rVDfON/pYuln5pKmA9gmETMmjnkpFNyecTABTYifivpG/48PeAyH5AtGWLh -Cy74G1R7a7g579S7occ4Enx5MMQZvA6xJLCZagxsM5vogIjQ5CwvrTA3KD7dQ3n4 -rDq1+Ycj6MsCgYEAio88WeR+aY0NLyJfok3ZCLdt56xFfRFk1x4DfsPqhymrU2vB -NQVlXsobzXIEHpevJuMQ8wMuVq213CkovAZdrR26S856CvSNUDnNXqsNPj0icce0 -TFJ61cPvwMZCsezI2gboZDYquhxvXREgWXnxx74kTYb/tSqDzEw6po6cj1cCgYEA -zQUnOuSky36s+aYcVD+WfWncuSiCmiOmHCXF/8waoAH6Pvj3mHU1eBoofC3lTyZg -W/rvsOL2nort5ZlrLqnRDmLpO1bXYaCUYiFmOYLa8853yUCqT5TAvlYsP23nUnFS -BnrINzjJEjxJbevuPx14ESS8YZUlVmwcngkig2tqQQsCgYEAyx6hnpLjhd3K0v5x -d78HarimcTRoHyldztubdVDV081KFTIrrWjQn1ag9eFOfctkhjngxlGHBtIQnLRM -pdYkCsymxTDju30EvD8bvOE9+SVgOFOpTRuenbepxltygtKQmGY/5w2PHQFJZjwI -tklzLp5QZNYfJeaPZ+frNRL7wxQ= +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDs8wg1+xrBWHcp +9d4b3dbIqHQiuYtKbXlGU52lKoU3vArMb2lu39EWMXrxg9Dn3joI3X8nUynGlB1m +Hvog6/RXggw2T5WtPIviAMIR3niQWS3gYrgpT90GVeIPIiuVh76IUFnz+a9wfxZ3 +rsXRU74kXOzWXVZr2pSH1HjSpWmR6HgjHUPi7/CRPL/V+/nvyY66cvxqgtOchGYh +ZO3+0zeGnXzVlXwdleCQnJ72EAOIyvQhrJhKiHvYw9/ZTD3VMHntBUu/QFB6a9IL +KxggB3iOQXE2HwPBqO+cJfoi4qTpywE7MOdI0q6MK9pr6r8TAGAlEPYoImbUFHIe +hJmPvSZDAgMBAAECggEAcSh3aWAsHN3kutNCaUh+RIG+RL8beEXjvuQ7yx8NU9qn +xY4Haf3VWqXysthisfj8+jLdgKhEsSMXDygICCTnIctnjaBgOMVLgHkgNWxrY3RT +Zm9+x6vuQBJPzS5iZhzXrz3AV7WMRUtMLYf3zMoTakY+jPkyRnuyp1OfB+obCqRx +TUfapIszdifeKKzhqZDDD049Dr7F8JqNfqd7BTWH1lxH8BYeZtYbRFUT1EoNBgNr +T2K7kzOct+Y8zFyYAL0D2L5QbhOvVL3vZGgmqUtCe28hvBBusVx/j3ObpZNDfoeC +/zlbROeX4lUBCt/QYiXLV3dtvMCO9Yhkh/rH5qTK0QKBgQD2u+pEQQWP9vxwKJ02 +hZ6j1xrUwn6lCjtO2+4aUVjRCfVYWUuKh+vDoxYFXZq1QTEbjjIkRkaNYi4P8sB/ +r9LII6Obxlyzg9I7CaO+YVeKg9zapOQHb/fumIP3Cv9SPJ+i5QloIm8XV9HQiHuE +ffdwuvMGIwA/tTHI8FuSmQqDbQKBgQD12QwpyToo1yT5FdRU8Ik0eD+hRsfUFEkS +4JZek0AWuNLixNcI9CIBnwyo3RF9U8jiH4KLAFq7caMSza9zgYHXwe/efmBnSUzR +C2Fn1ZtNmnwrB9jkQcO2AljpYkOq/+u0If/Sh2w879Vfj5JoD2XXZ96WD6ZVufCp +3UVxi+KSbwKBgDWXjYPzx5yEnkJSYAuGrHWT9G0ALff1J/qyBJ7MyojlK9hb1O+r +JEejNdZGhaMXHO6KTVPZu9tb1vt509woOZVlQFVGmb4YROKz7lAc3qHkkGUhabI/ +3M+Day6OwfR/3IDKVOe8ivq8BYPREVsu7T8cLEtJ8PTli4HEy28lNMZlAoGAGXLe +T3UFEDU1HpOj5yb/DO9lpsws40c2ST32UtPrTm7TZaX6nHKpEoHkTZaUuORzZvTc +Dod63OxmdQvVnZ9KUYtByUZLtGViDkGfU6BI8z+Kmum1CevE1pJnocEKQCk9vOfI +E4LxMV2iOMIj5aGyaRnXcLEuBrG4t9yv05EnYwECgYBnPPyG0fX/gfyfv3kBzhcf +cpVSLSQhpgzuxHUac0sCGTfa+O6u14inw1DeirrZQJTTmDAFfFThg883x0/rqbbi +WDaSG0Sw0j/m0tJgkz1ENRwFg+5KBfjthODJ0EhvgOoNbK548boouzGe4GtGNuDn +d/HI8SbFU33L2FdVWNoGyw== -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDsDCCApigAwIBAgIBAzANBgkqhkiG9w0BAQUFADB9MSMwIQYDVQQDExpJbnRl +MIIDsDCCApigAwIBAgIBAzANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt -cGxlLm9yZzEZMBcGA1UEChMQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECxMRU2Vy -dmVyIE9wZXJhdGlvbnMwHhcNMTQwNDA4MDEyNTM4WhcNMzQwNDAzMDEyNTM4WjAd +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAd MRswGQYDVQQDDBJhZ2VudDMuZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCvaJCmw0/fy9rmPGtda/9wOlaqiozKac51fJbfDcdiTV9k -Adoln28td4vAXqCckIWhdeSAyF1MhIpnGSsJuklsHTJ2ZfospNwDY0NqYkejAzT6 -RM/jblY+D8/Cyp37UjoLi1Ri8PEbnjbgYMVKn51nZlBexHp1CpFBvjm1nd7rt3Ll -wZ6Hgbg9aLSJYKWucvsFNteeDuqte6fV+ypO/VtDGaLJS9e/3X9kNt7lU1tTBLyD -KfUOhpDBR2+fuoWJ9YADFiyK7AadAV+d+nlep2UG6MN8L0H0ZgBp73Rpxb9tgIWr -istMboUvQghFfZ4n/KTa4s6ooUe2tzMxFGsqaus1AgMBAAGjgZowgZcwWwYDVR0j +A4IBDwAwggEKAoIBAQDImAML5qGenaZ1oXlNyDTnPg/0bq5ft2jTZjPTUijBiuYI +ZqANvqjf+bicC+9LdQwpRoIm4hS+cnvaEdXSV73cWjgFzAFJTBYaRXLst8/r0QlU +BNkHgDpgCFVwR+YIEmHeesprzId/uDE1tf13IRI1NpDgU+jlBH3wOiUDr8QbPWPG +IUV4bbEzLCGoP2fO5Le5p8+NdRI7XCHo7ac8eRL90pyBFBKzdc93cYMI7PvimuX7 +MiVObjil3FF+tsdkAAAJezLKlW2CKjRUdHCr5/2WIRpjiIs4k/hCDToBm+8ye9m4 +3NWn68XMIOjCiXl8wjdbTA3z2A0iRE9hGh+pYhQzAgMBAAGjgZowgZcwWwYDVR0j BFQwUqFNpEswSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9w ZXJhdGlvbnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEOCAQEwDAYDVR0TAQH/ BAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC -MA0GCSqGSIb3DQEBBQUAA4IBAQBXOZ+j8MRJgS1XBriZxL5y5GTCxZTmgdv7akvy -RnDHN6CPSPDG3HX0Db7Ey1kcQU/d/N9PCDyNGFUxihfq4CjqJpQAarZ+oYBCO+k0 -367Xw+iKm6JHK3CZLT9GG7ikz9wAb5E+O3PMWePAtqSOdHmEF+K+8O+WNHe/Slzz -BvY1m0WXXxEtbkKOxVCZ9oPxO0dZd+CsiaBTuMp4TX6218NIWNGYixaVNCOTKT/i -nuE3naOGRZ++y/clYrrni18WZC4jqTdZn57Bho7gf6nR9hFt2FaIUPagS37oWKF3 -KwpsbWEVMcdjbMUGuG04WclzATUbqJt+m1ueiK4slb63O5Z1 +MA0GCSqGSIb3DQEBCwUAA4IBAQAJvTvSc/J7QUZXtwIDoRSpEw1BBb+AKsa4D4ui +qQXxiiRAe+HkSVvDnYU6gDTiEwtjccC/78KYti6QKWsYXhdW298ALdP9guAL78Lt +A1olS9hNLXMiqWWEGPu0UAmNuTpdiwYbTL6vb7tcgBsnXONS1N8i7N1SRNyGRURL +E2h5n8LGupXtum71XKMpNDT7XKKh75O8jsfGwkl2g4DQKmU5+9X2RXX+btooIsBi +xgsOcq6Tb1qHdZRvqH75iypj8osiy+4cflgvdjuElHBvIlFRlyJyJPhAPk1xWNRq +5+tDXG9cxNfWCGp2rCtVTxx2LGkCj1SQulMc6y6OZin+J/QQ -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSYWdlbnQzLmV4YW1wbGUub3JnMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyJgDC+ahnp2mdaF5Tcg05z4P9G6u +X7do02Yz01IowYrmCGagDb6o3/m4nAvvS3UMKUaCJuIUvnJ72hHV0le93Fo4BcwB +SUwWGkVy7LfP69EJVATZB4A6YAhVcEfmCBJh3nrKa8yHf7gxNbX9dyESNTaQ4FPo +5QR98DolA6/EGz1jxiFFeG2xMywhqD9nzuS3uafPjXUSO1wh6O2nPHkS/dKcgRQS +s3XPd3GDCOz74prl+zIlTm44pdxRfrbHZAAACXsyypVtgio0VHRwq+f9liEaY4iL +OJP4Qg06AZvvMnvZuNzVp+vFzCDowol5fMI3W0wN89gNIkRPYRofqWIUMwIDAQAB +oAAwDQYJKoZIhvcNAQELBQADggEBACqpOYBQRXhBBRs64ctwvV26jrwJMd3cyej3 +JYiB2vwr17pHKlwDlM9gKp/ZvfNFs5K7Q53xUK5bm0xPkXNeuicvgCCUi0h7hOdo +902JZ5lH1nos+ZV42Q+RjmPC8/X354F2PW0Be8gL3atQw6Grl/Ffu5jcynPe7cxu +GYodZ8Vzn2uAOWURSaQqu8KZA+hDzCrE6faO7DSJKZ9foVS4aFnsoUcCeTGnJLh5 +DmQKO1BGC1gdF5S7e+Jld3705oOeQIjL4JutkuDZR9QUajTNn8LtlncjffAdatvK +3sNceBdxXPy/XxVyVYkW7Ybq9RZw1FanjUNMEj8SzSBWsw3819o= +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/agent3.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCvaJCmw0/fy9rm -PGtda/9wOlaqiozKac51fJbfDcdiTV9kAdoln28td4vAXqCckIWhdeSAyF1MhIpn -GSsJuklsHTJ2ZfospNwDY0NqYkejAzT6RM/jblY+D8/Cyp37UjoLi1Ri8PEbnjbg -YMVKn51nZlBexHp1CpFBvjm1nd7rt3LlwZ6Hgbg9aLSJYKWucvsFNteeDuqte6fV -+ypO/VtDGaLJS9e/3X9kNt7lU1tTBLyDKfUOhpDBR2+fuoWJ9YADFiyK7AadAV+d -+nlep2UG6MN8L0H0ZgBp73Rpxb9tgIWristMboUvQghFfZ4n/KTa4s6ooUe2tzMx -FGsqaus1AgMBAAECggEBAKQDEtZLY8EvnNORBRcKZ4MpmWXHPRP60Qh74ZpZCOi6 -zgn1zWNGa69Iy4ypLmaWn8y0Ll/YeZyVhdAW8W4cGJEVz9xzR/Q8hlhIlX3AvZkH -455rfzL6NXu8TxilWgjn/sxSkCoffmLDki5FbdRz4LsRKO8jqhkMM3wUgWN3ZTxi -9HfvO9cqKX49TRECs8i6o0vjSBY871nBXS0PQb0IRyEPnidk3fMl6KkYzAy4VQB+ -vlFUnGTFUx9wStAEIG04v+X2yBXiP5KeCqAwikCB1dHZU0iXliadnjn1LD6An86p -1kkUtZ54crR4fz/SELYLivApYLDPidVcAApS3kZCN8ECgYEA2tSxzx+4BjjnPIga -2hzVX4QE0qsB+rCsxBKFPRvLWu3coBdTor+t4S5Umft9nZtyRRa72JOZUuG7yjpi -rDxqPo7s/3hR42aelH2oYKTlz51oD2gVj7S+NoIZGynyo8EJZvN4PmAcOU4cQoYK -Xyq2LPpdM4ng1KbhGKENYX5ToGUCgYEAzTPCEeUIYLLb+92p4zMkLkmATtQOhcGb -v2VHWeeK95NEwlSlUDnmfEnJzOA5jcV1p367iC1u4+SAqX4poUrMbQtO6YjaLJ3n -lQM9WDM4Tk5n+K3xCWFbcYaaBvfoVmJ6IdxmqN3O44osRzX1nEZnQRbADBT+/ncP -y4EMBOjCqpECgYEAvt3JgQj/b53uQqPMF/YSPc9ejYlOnqO+7P3ibNxzUIorZw8x -icB6HvBUJkJu3CsbTht4GH0UvG/bZZXrPqMrYAk1udXuRtPY+Vdop3LsZ1u5ycZV -GBTj25M55rF9y0qxsrQlavJVa74vc+6J2VvdrflplWxrR8+OgfbnPuP6eOkCgYBB -Jgicc9G4GbEcAuE4H/mBtJZG8K53quNO3dHMamXFen0fEY62rNtPZIHsSNgc+a8D -8YmKj5ogQmDS7xo6eivzBt8T5/4UokFvHuswAHsE6YG7A0Z+iulkXq6yPa0HGBbc -f0QcoudXeHvcU/ZZmkSX9qgcbeITWiuqmZr+SAPd8QKBgCmQbKVYf0DZ45duXrl/ -UaAg4TNOgbXKP5sCBMA3Wl4gOItL9umJFmh2QHZRaieFt7qlYCYsLul2CzOOaqqC -nA6US7SMpq5uoxiimwjY9PUF9WDzwWFFXxYyphOZI5fXKgXCBOSwBMBPioYWxhQK -amoP2hwuVMpudtS2nZOtnPSJ +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDImAML5qGenaZ1 +oXlNyDTnPg/0bq5ft2jTZjPTUijBiuYIZqANvqjf+bicC+9LdQwpRoIm4hS+cnva +EdXSV73cWjgFzAFJTBYaRXLst8/r0QlUBNkHgDpgCFVwR+YIEmHeesprzId/uDE1 +tf13IRI1NpDgU+jlBH3wOiUDr8QbPWPGIUV4bbEzLCGoP2fO5Le5p8+NdRI7XCHo +7ac8eRL90pyBFBKzdc93cYMI7PvimuX7MiVObjil3FF+tsdkAAAJezLKlW2CKjRU +dHCr5/2WIRpjiIs4k/hCDToBm+8ye9m43NWn68XMIOjCiXl8wjdbTA3z2A0iRE9h +Gh+pYhQzAgMBAAECggEAUmCrD54dTWhsze4MRPctUrHYF4fEsKY/tGFKc0RQXHtQ +UK7kmABuldyWBe+YzCI7ZlAAbzt1TWjyl/GvH/zMicu7KhZHiPRoN03CDpjGGI/2 +Br0e68erMXj6rWu5eiMich13N6H9G2RnQSmF+ABq29OW16leAX9AwQD/5m2uWYi4 +XiXLX0CCOLjugq5Z4qCK+iT5AigYNxjUufLXx7dWw0krpAvX8AFHZ/dqbJrdzxv0 +1xaiS6AX6f0HSDqSMEd+ZjUssaIWDmEoRpjeCpmKxwAWtN781w94SLl5Te71kxyY ++b9VNJZN25TAQHARczz2QtCzWVmA91QX/aOWOu90YQKBgQDn1lQ9WxByTX6qS6Re +VHYzKgUriNBAXu9q7T0d7FxjmT1GfUPT2PsVRTb6yUsd98G+5D3FdvuGbxYRj1Kz +TUQFp9TD2tzCbk0qID0gG4x7uJ/RfvV/OyYSNoBMfRRN4PcsjLSkPL6hDYPgVqim +2nZWPiOYpiv0Ht/Bak+54H2fSwKBgQDdgBLypWv+c+MBftb+b26ZAT6VuIBFoIdP +t0QV7gQVRvH9rUyaSDb4lOVhCnL9J39higOZXcnnjiQBOLWIiv2B+/RJxpXDVASy +nzGVIT6sanE7TQkg+PSvzp7PRp/orNRnQfR/Ocu9GveMbv66mEReq+bTTF7b67LJ +L9ilwYiFuQKBgQCS9VrK1dnUCuma+34CyZlvj1hdo0kXNNahOic+u6BJBk9ASpFf +TbDDZ++VA1pZcb7fyYCalnSUYABmOyraO8U9rIiDak379b12UZ9NpgjkOEEepFLs +Rss4SlIDqlnfkBY+qCat4U+ZeZ6561TXB65xtswLrHI6OgPub/x/wB0BhwKBgQDA +S7And/WJspIhejysJ5Gcw7fRw5uY1wSh8Djr0Pc6gZ0U3p7iUk9m/90joi6yGNg9 +ldQKEaJ3pK8dYF45bHEpp9MtOXpLWWW6rPoevf3rMX2cPgTu2zOSbY7x5B5voZvF +CT16NnLqeb7v7Gy5EfhJOHPsfSDdTIKqpx8uyBX+AQKBgGLGIQFnX/4DVANpv4Nx +ssSHRNg/w1RllijaTK0dZaGsp1XVJVSlKEljWcTJkpGOMyKFmVnvbjEROOqhATDT +iwLPBaJ3391TCqiEXHbutN5NlL8Iy+aqMS8CRL+8JhFpgN56GtcP+4Y0BpApfBga +3trJ2pT23VNfbf8zFwNfKsnb -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.crt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2TCCAsGgAwIBAgIBBDANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow +RTEiMCAGA1UEAwwZbWFzdGVyLWVtYWlsMS5leGFtcGxlLm9yZzEfMB0GCSqGSIb3 +DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAK+DZO+xbJow5kzEP13hC2XkPjY5MZZg7sX5VuGtwhSD0vberSxijokB +RR9U5RAp5AmWEpbJcNuL4wUyQ0yA/E0FSc6BhDwT25aA2l8CiIGpMmLmMw4M80K1 +SXuHeXU/g+7MBmaYC4AxYbsxr1us+c1IpNYl9rO/4ZuUIgOHKjnnBHKYZqpNVmXS +4ss3o74rrHkIWLvBI43Mz4Ct2LZMSkaqqESvIfBKpW9LndtoKyIaaoxnXivC7oiG +pbEfot0pvoCNV7Wfj7zFVQUdKdJTa5XHvshXoFdrnNHfZKVb6AIAbvZidzcBuT54 +Ib4KFjIV3VxfJkQ/U0VTn4zhK+t2EuMCAwEAAaOBmjCBlzBbBgNVHSMEVDBSoU2k +SzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9u +czEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMBAf8EAjAAMAsG +A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZI +hvcNAQELBQADggEBAJ083++L/CwcyFhC3FvaRX6dYQB5LlqPQs7E6GRZq4wL2Boo +rNeWVTOhhjdxjmbLBLJ/VDOzxW5mCIVil8mHbTYUrmt77lfXkeB1Gb4sx8s3SbdK +10mmAhrV7eLjeV1Y/Tt9G4ZU3RbK78O/GXCg5Vp1S6GPUBMxlgm5K1IibpKxz5Cz +yU6EN0seAhKB/MWiSWILcJy8sbySThLDy0wLzvUDFqHlVvj3XtBd08qp1Pov+r73 +pBjgDHJvunxJ93DDfhiMPgoOIMcvijZmOQ+2cFfbhwlyJH3m90PbpaW8H0BNFBLT +csvsTzHsS78o9usu+Sv/NpmDnzvOu1rzSCMmu58= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICijCCAXICAQAwRTEiMCAGA1UEAwwZbWFzdGVyLWVtYWlsMS5leGFtcGxlLm9y +ZzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAK+DZO+xbJow5kzEP13hC2XkPjY5MZZg7sX5VuGt +whSD0vberSxijokBRR9U5RAp5AmWEpbJcNuL4wUyQ0yA/E0FSc6BhDwT25aA2l8C +iIGpMmLmMw4M80K1SXuHeXU/g+7MBmaYC4AxYbsxr1us+c1IpNYl9rO/4ZuUIgOH +KjnnBHKYZqpNVmXS4ss3o74rrHkIWLvBI43Mz4Ct2LZMSkaqqESvIfBKpW9Lndto +KyIaaoxnXivC7oiGpbEfot0pvoCNV7Wfj7zFVQUdKdJTa5XHvshXoFdrnNHfZKVb +6AIAbvZidzcBuT54Ib4KFjIV3VxfJkQ/U0VTn4zhK+t2EuMCAwEAAaAAMA0GCSqG +SIb3DQEBCwUAA4IBAQAO5+r9Dw3IAxhrrQeHA4Gc4z9ZAUKyPkS6saU59eR/2i/P +e+j/pSMG8Mwbm/Q+AHkzeQfPKA01NOs3Xdtx3b1AlD5pP7ZDOHwRFaQIAukaXGmh +8Ow09pi/4HPvdzSZTLnoq3YoSrmKGRWad2WFQ80x7mo2vC+4WSg4VnKMZ0CjF4fO +RxY1fCe7DMoEVAo9ROC1yDjpVvHyWemCSz2ckFEm/dzOBoZzKW4isPYccmfuuI/C +D27/TQcWOkbU8LgLuSylzEb/WOW7RhvQuhZIj/r4aHudIemfC83pKxZAp47wpnOk +a8KxcizMloGxfNCbxvkKTcZ1jyz4dL0xzdyGXhRR +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.key 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email1.example.org.issued_by.master-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCvg2TvsWyaMOZM +xD9d4Qtl5D42OTGWYO7F+VbhrcIUg9L23q0sYo6JAUUfVOUQKeQJlhKWyXDbi+MF +MkNMgPxNBUnOgYQ8E9uWgNpfAoiBqTJi5jMODPNCtUl7h3l1P4PuzAZmmAuAMWG7 +Ma9brPnNSKTWJfazv+GblCIDhyo55wRymGaqTVZl0uLLN6O+K6x5CFi7wSONzM+A +rdi2TEpGqqhEryHwSqVvS53baCsiGmqMZ14rwu6IhqWxH6LdKb6AjVe1n4+8xVUF +HSnSU2uVx77IV6BXa5zR32SlW+gCAG72Ync3Abk+eCG+ChYyFd1cXyZEP1NFU5+M +4SvrdhLjAgMBAAECggEBAIcVwTU+wJC4fDivfvn5gLVOg5dmU8UjHAOh0f3mp470 +mwXJA047GOtX3+SqiZb0NwAt0mbd6hAATZ2tPuD+EbISFOltA+LdgdAHoosnvvrU +x+QYu0jgLMa2EzWRYqkcVqYPwAlsksV6vHbAWfBcZPNcKwprs8JLOBBvZfKDMLvY +zVf0Dj9V+S6YQPQjkAa7uShXJ7sxt53RqwI0ueR2h5ZBLVm0LvijghDvSh0O+II5 +4j/dkBPaXaPcf/pPMUiPcdj23RguX6ArAHzbROkclg6J859pGHs3GCsrguE2iKgR +s/AK8tLj304h60K1vJ5tZqWokDqBWS4bwyKLCE8En3ECgYEA2BRMyHun6N+xXQTi +SjCTIDfBY2QfYL4Nc0o47ds2WfXnmsoLUg37F9NXD3iqx5WmkZk/AjZ73dElEbBY +P+/Wv8zgew6Qoa5s+bd36OxFiv0NAQjclQ0QPP+dSoRuqmT+0YI3Yf6Vl7vaumUZ +sh6L2nTWX+Dk/0snQrjvDs3CNdkCgYEAz/B6/9FPvW+Mi948MjQLa9wDtL99PuDG +AnfL8SXsSTgwVvgWPW8UhP6I7jRTirvJiliJVQDAHgmlHCwvvY09v3eoLqhznlYo +E+KiqUdzVzGv2AaUwC9+8IpUGT3qFFfvgvuOTe/eTB8zMoLR5GSHlcLcC4CX6umW +dEDvhBZVbRsCgYEAtJfSzMS8w18iJ7JLRuJuNMEkumMV7o8pQrBIAFP0ix+A4Nf4 +ui3BLFqQKvgZFBI5UvsXtEvBHtUL/Eq8l0Nm4orjGzZDHz5fCh6S83N1dAKm+bRk +V5bBeZnR5lPlAjjC2GvSye4zLMvutVaGMV5iWMTJ3vxhqBUxzQwvkNnbdZECgYA8 +y4maq77NmwSTjDk7UF4afewTd30N2jfqVs4oxjasPaJcexI9ifGM5MJyKphWUosd +SL92O87vuVAtyXl3yQjJpxlj8tpC69ux3KxEI+DEGIHZ/iqVeg16FGd8Lc73fbJI +MHYtsTjkNqcIWJk3VnERxzQApOtFwl+7w5SDoOf/CwKBgQCQh5QQCeWbO9ndQfGX +ztCxCmUVB4/8GfIkgx7/QECNLJKmTmqShG8dRU/SVzjIEnEOOoKsdeiPZAOvKrI8 +jsz7N6cA3YRkbmQlku/FjWb+0CL4Kms+bfotisQA+k6ipjj5yBPmB4JVJ7xthpPi +MMufQQcUhmOG9p3WXv0f71jPEA== +-----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.crt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2TCCAsGgAwIBAgIBBTANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow +RTEiMCAGA1UEAwwZbWFzdGVyLWVtYWlsMi5leGFtcGxlLm9yZzEfMB0GCSqGSIb3 +DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAL5w5qggNQUtGSgga5SPGHr5J8uIrg1IE/WR/p83V2pV+QyAJQChmIhT +sBx87INcl89jo39a7LWoeXa3gUaXgP+6JY4A7E4nEbTveqBr+vKLdeZjyC0DwhIx +4Y6scXD9O2OeXH/qKdEHLnHAtKZBFIHY0YDDVVOrpS7DNtCZe4KTuX/97dxl+Qez +nejIIY8vhYz5Cnhfad5KEKIuoKzj25ZGxcPr3/BeZ8IOr2/vqW6r1qWFCx/7X23Z +VX94c7RYacqiixIUAf1Q6+9EoVhLUctuwnfTCqb5HX2zKrcsVAG9tirg5bQeqF9i +MQDblarSN1LRbItL/0q7ZRSgZ8kMnKUCAwEAAaOBmjCBlzBbBgNVHSMEVDBSoU2k +SzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9u +czEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMBAf8EAjAAMAsG +A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZI +hvcNAQELBQADggEBABhJx2HxQf/RMxdTzikFAqFL4e6xlOSZtTBO2jgD6gGJiBmR +qHB/2XXhOg6lUUm3woVCC6CCNYv47OHtA2geDA5lBEfLGQJOPMMCR/z98QKX6jVE +SVQNy3DwNmuWBTCBA3L9ArQ9FL2NhXnDhyOjVM/u4/dpYNXegzeA4du8BVblObo+ +UY28OtNkZ72MSSGV95kAiqltBSYEJhFw8c57kAeIrgOgWy2XKaUToLICSV19BYAM +IzR2HLwtmAYvga59xqSKyqmnJXiZpJqEae9XwR4JsWuYN5nwualobsAR8JPI46vU +05SYeaz1hwYollWM0iIQcXwArWFyvLXGFApQdQc= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICijCCAXICAQAwRTEiMCAGA1UEAwwZbWFzdGVyLWVtYWlsMi5leGFtcGxlLm9y +ZzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAL5w5qggNQUtGSgga5SPGHr5J8uIrg1IE/WR/p83 +V2pV+QyAJQChmIhTsBx87INcl89jo39a7LWoeXa3gUaXgP+6JY4A7E4nEbTveqBr ++vKLdeZjyC0DwhIx4Y6scXD9O2OeXH/qKdEHLnHAtKZBFIHY0YDDVVOrpS7DNtCZ +e4KTuX/97dxl+QeznejIIY8vhYz5Cnhfad5KEKIuoKzj25ZGxcPr3/BeZ8IOr2/v +qW6r1qWFCx/7X23ZVX94c7RYacqiixIUAf1Q6+9EoVhLUctuwnfTCqb5HX2zKrcs +VAG9tirg5bQeqF9iMQDblarSN1LRbItL/0q7ZRSgZ8kMnKUCAwEAAaAAMA0GCSqG +SIb3DQEBCwUAA4IBAQA5VE3jNZ8m0+R0rVBkOHleizlI5SQm1Zup6fEJIyCGmrEU +KYjF1H41MHxjvbs1pIbgLnzWBg3HBKYF4gBlsSV9nbIl2Pe541iOgj+ASc4T0VeT +yyiz1hBN+iZqp1AaYDQr6rN0aSUf/z7NhS2Rm5I9ZuyFOm8PWSgpQPAGd4Xc1qo4 +du3hUYt3vMyKIeEfQYJ/FyHo79PMbUeFXTwex/7NCmxfIUvO5qtg9t0pS+w73DYu +2T5z3Bq0mZBAs5gWrCTVBZ+HE1xBQxVgK+Qcq9Qg5hQbVA7jSTNAa+YfUyb1gDW+ +rV2e5L/j4M075s2dB23IeaVlDMTIexnIPdzvXJkh +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.key 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master-email2.example.org.issued_by.master-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC+cOaoIDUFLRko +IGuUjxh6+SfLiK4NSBP1kf6fN1dqVfkMgCUAoZiIU7AcfOyDXJfPY6N/Wuy1qHl2 +t4FGl4D/uiWOAOxOJxG073qga/ryi3XmY8gtA8ISMeGOrHFw/Ttjnlx/6inRBy5x +wLSmQRSB2NGAw1VTq6UuwzbQmXuCk7l//e3cZfkHs53oyCGPL4WM+Qp4X2neShCi +LqCs49uWRsXD69/wXmfCDq9v76luq9alhQsf+19t2VV/eHO0WGnKoosSFAH9UOvv +RKFYS1HLbsJ30wqm+R19syq3LFQBvbYq4OW0HqhfYjEA25Wq0jdS0WyLS/9Ku2UU +oGfJDJylAgMBAAECggEBAIWYAdazMT5+UoV5qL5AW7X4jtgS+7ZJd+bvEaVzVxk7 +UkhYpJx6v9vyiTqSz/+etsg/Z3Rxvlf45axIMqHoqg+675lcQGjas2swC9/5n8MB +3iVOniAl4MCOyWIiyjmvCzho4Q7Cn68I0KXvv0pZoR8tkFo5rOPTJz9UpmFTKfeA +nyufEPsIEvgISjW+Lt7ZjQghsqQCyjVfpUvT2La5srz0yBbNp1QEcP9hqH6d6zjV +dtXB2JfDk3x9GkBi/DHCNnJbseF/FyXI5ezqhVkYvxVA19uY1fx3nB9gyurSMY3Q +KdvnGK+m/eVTJ2UZXlX4h5+7w7hfeH9YBuVmhlWj3QkCgYEA7UgeTJCqyPPQGzZC +U/c28z2NTO/sEZn9d+2l5N3PiIoZFlsNNDkpLmCebdYkeCtLigfR1XfcQ2s5B/e8 +so7G84ZqDrtWR4mEHS/8pvORvWN1gx7gv+X3t8yWkKUzOsLaEQBNwM+kiYN4VWs7 +MJnVAO0egHKWO2uplpLMjVmBA+sCgYEAzXbW3LLXHmRYNgJ4l24Sf50KMH/EX9mZ +WNx8gKLi9VE04oS2i12Dk5bQLXbJ5sbMASK2nZCIcdzp99uyjWPl8a44IMQZMNHc +T8GPzCI0Z9v2i9InnfL9RfrmCpY0Ulf4gY/TGVyfMn3Hr66c9Ol7eMsAg8+5clAD +7dDP/YP4Da8CgYARtU8lkapdQuzmCv+aOpnsP0y7UDCDA8YJ3D+EPBWP8BZ9N6Bd +pGJxZp6MPe4M1dZWX80GLGLQ2UCxWojHnqY0aHSj0tFWl2m3r+wgHY9j3fdYKMbH +K4d4PRmDhAC1wvL5T9d9q0FxTdF+Nnl9YA2oewnKtkKF/rvyU9RWPDbHrwKBgQC8 +VlT6FQ1PlJevWnCEDqfyRldSwauWWPZSGDogCC+Ww4uAoDpcYan8nd/R7POCEXRr +DhDrOdN43BGz27U+GfZrpVFZ56dXsucKTNmtOyVilqTW4hrilBxKC4TW63ymhnEL +AnV06Awd0mI6zgqeDW/a9+pO6RiKioIveptNPa0hjQKBgCBs2yZ2BMDVQ1/IlxSA +Ot2ufhzw39VYG66NX/LaX6ii+sBkV3WsUYpLCbSadKyHAJlBXz7V/OTRvDx69Zy6 +MAJuSuQW64EJzDEGO/OHpIzel6pmb/C1w55bcDf0FASHhnjfDX9P68woQX7J1PWf +sy5mAMn4VfEqifyN8oeaasy1 +-----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,24 +1,24 @@ -----BEGIN CERTIFICATE----- -MIID8DCCAtigAwIBAgIBBDANBgkqhkiG9w0BAQUFADB9MSMwIQYDVQQDExpJbnRl +MIID8DCCAtigAwIBAgIBBDANBgkqhkiG9w0BAQsFADB9MSMwIQYDVQQDDBpJbnRl cm1lZGlhdGUgQ0EgKGFnZW50LWNhKTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFt -cGxlLm9yZzEZMBcGA1UEChMQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECxMRU2Vy -dmVyIE9wZXJhdGlvbnMwHhcNMTQwNDA4MDEyNTM4WhcNMzQwNDAzMDEyNTM4WjAe +cGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEaMBgGA1UECwwRU2Vy +dmVyIE9wZXJhdGlvbnMwHhcNMjMwNDEyMjI0MTU4WhcNNDMwNDA3MjI0MTU4WjAe MRwwGgYDVQQDDBNtYXN0ZXIxLmV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA5K+/ngO2GiDGyd33t8jROHqeHEFrEtm3+T7ap6BQNj7+ -GcOf5GB13SiBLoyWaS2wFCBOJbqlXwwjfKPbYyWxCMk6xKYAhmOC7he1auNC5WUa -TAkze0lqnyOuJCfIgGEJm7AGZZ72WIP60u60+YFaXcGkWMCZnK+7gpHtfaMdkV+P -/6Int1DRikUGYCFBqZoEi7S8aaOO5S9/426+AYeUCSv7huIRY2i5kzfbo47MhpNI -uQEIroKDH35cpXkkAqV+/bttfkGBGNPdv5ccRfsoWs08HeU/WyZGPeLBzYUi74YC -GjNsKwfn65sJSnYyQbSbtEcUSvXr8SOPNaX/WCMcPQIDAQABo4HZMIHWMFsGA1Ud +AAOCAQ8AMIIBCgKCAQEA6RLLxx3df3Ln6TG3iORiJNsQfZsRa00XgGhzbdTGOJ0v +X5Yjq3igQeGVd3aCqAEivaDmk0Mk6mWyH5l1ODn75PdYUFMVblmNaCZ1sY6lc3uJ +qWPEz6AZYojkLrhumtInUD6y9N3q2CllgT/B1yb4Hfs2WsTGtZjmYOqdKv0gaTw0 +p46Uj0UrE42GOVjrrIQfomq16XwUDh1kqfIxuob4J/ZHN4pHsFtIvh3F3ZD2OGPi +YLyPGyP3C1ssUnCoqfMM2TUO9LGeWquRvCLoYe+jA5Q8lbDU1NjxrRlfS+LyeCgn +96vWWmh/THVnaCiSj4O68pZABsgmHhg+diC8t+/sbQIDAQABo4HZMIHWMFsGA1Ud IwRUMFKhTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0ExGjAYBgNVBAsMEVNlcnZlciBP cGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExDggEBMAwGA1UdEwEB /wQCMAAwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD AjA9BgNVHREENjA0ghNtYXN0ZXIxLmV4YW1wbGUub3JnggdtYXN0ZXIxggZwdXBw -ZXSCDHB1cHBldG1hc3RlcjANBgkqhkiG9w0BAQUFAAOCAQEAZHIF2DB1ogCiTfcT -mM50IxDsUGWit3916FdIZEpgUcp+mMd0vpKejzNy/G1iriBErHujwxUPJPCFkyZx -5Uly8PoTykMGZPFDI3OaDnfsWtX+JV7P23PKgKYsUea2ewj+kUVWkKI2GhLV6+We -pWrzbUFFfATjMpda2k9f8AaGT5kU3L16KrCa6X8tuE6FTeMrkYHZxe72rufZOWw6 -XjSJ2wqxHDp+iiZ627Qn6Z1DA3b2XowCPPIaVnmhlawhQKDGrrh4HfuqQSI5LpwP -7Wx5oNNSC7qGJCVFWAamkQiy7JsPrrM/egNDxSV52Zkhp8AjDrYn4CTrdbd1CINO -eS/Dtw== +ZXSCDHB1cHBldG1hc3RlcjANBgkqhkiG9w0BAQsFAAOCAQEAP5IbZHIFdRjh7f1p +9SHcGEJ9O/OYZfkmqzKfNsnQ3q/Honz3C9SxWkAdOTSQClayi/x+RkOL0h+50+Mw +X/rC3qkIRce0JV+Uym/T4WsGz4Oko37xlt6Mlm9x194mTk3wcy22kY6jEOfp+YO3 +Chsdw62yyp09v5YkwE9obPfSB3LshgabLBDsZPOqRM1IFmpQ7gkuwa/RlvQfidrs +G9A4EzbWAt8d7v59HsL9u0+p3NT7JM1IH3pU0u5UWQwrMVkpiurtrz+ysTXp8XPT +QUaA2LkGb4omiW7n1FsablFS6uByD7v41P7oDW5dMkOsKl+mw1Qeh/wZOplmoQVx +6xT00Q== -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYzCCAUsCAQAwHjEcMBoGA1UEAwwTbWFzdGVyMS5leGFtcGxlLm9yZzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOkSy8cd3X9y5+kxt4jkYiTbEH2b +EWtNF4Boc23UxjidL1+WI6t4oEHhlXd2gqgBIr2g5pNDJOplsh+ZdTg5++T3WFBT +FW5ZjWgmdbGOpXN7ialjxM+gGWKI5C64bprSJ1A+svTd6tgpZYE/wdcm+B37NlrE +xrWY5mDqnSr9IGk8NKeOlI9FKxONhjlY66yEH6Jqtel8FA4dZKnyMbqG+Cf2RzeK +R7BbSL4dxd2Q9jhj4mC8jxsj9wtbLFJwqKnzDNk1DvSxnlqrkbwi6GHvowOUPJWw +1NTY8a0ZX0vi8ngoJ/er1lpof0x1Z2goko+DuvKWQAbIJh4YPnYgvLfv7G0CAwEA +AaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBG8OVju6WneRUM7/m9dERatLoaWbaUwy+Z +U9IztgtrT5n4wgp8mCh/Ue9NxJRTtxh+AvML/lb7J14xi9D3X7dizbpRBknLlRYn +ctIxXiL0NopwlqEUwkJAZzEvTX6HN2IJ1pA1GHzqznAHBn/waNdhLBy4T4by08dB +Vvclkr4lPhJkHBw0TRLk4SRz5iLQ8Fl09a0HLhqOgMcoj3mTeR7PajjAEI4c9jau +Eivt9SjFeHXF56/uCJV5clbDQr97qUxIfwN/XtovWCPf6NZNulcLK5hBbBV+oR/9 +DXtRLseO+Yn6v/B1FgEKO9pBsW7/tYTos4WR6C6Kws/wLm/pZ0g6 +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.agent-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkr7+eA7YaIMbJ -3fe3yNE4ep4cQWsS2bf5PtqnoFA2Pv4Zw5/kYHXdKIEujJZpLbAUIE4luqVfDCN8 -o9tjJbEIyTrEpgCGY4LuF7Vq40LlZRpMCTN7SWqfI64kJ8iAYQmbsAZlnvZYg/rS -7rT5gVpdwaRYwJmcr7uCke19ox2RX4//oie3UNGKRQZgIUGpmgSLtLxpo47lL3/j -br4Bh5QJK/uG4hFjaLmTN9ujjsyGk0i5AQiugoMfflyleSQCpX79u21+QYEY092/ -lxxF+yhazTwd5T9bJkY94sHNhSLvhgIaM2wrB+frmwlKdjJBtJu0RxRK9evxI481 -pf9YIxw9AgMBAAECggEAHs39xc9CLGrV3ENmJv+KD02VFsFJJxTkz1JEKehSZbw+ -hkHvu9eyiMP4Ask1SZ255jwjyrlFpXQBI7z+xHBDVKemnu6ti2zEKkZPTcKnNDdW -P3/Ny4GsNxubTYt+Wqy3mp0vf2jWTj4Y/2jgP+vIvHBlLv9isdSKMaBumUnrpbU0 -fnWJ5fz5yyNoSaaH8+UxOJz/EMOV/Crc2KuwULCpPWbJXVVFhgQ2o0oCjBC6RGUx -C9RyjQCOiEWI7Rt9IeQAqmBqIP1TFZeWlCiJEXIQi7P7y2LtpbCSu2LXU7bsTKxS -uXVGmiix5yNiyzljsGMDCwBiAzoKvA8x+kEv/Q9qgQKBgQD4zF5vyuBN+Palgcep -aAn9a/og6kWLYslPNEvWYkkSWi8R+hlWgC1W5LUXpply7Pj8ZdR82McPrRjDXzsk -wz5MofEWkB3O/qgOjY8BR7N1pMddBRV+eVvLZMTnyaCdkaKCqo/L1a18ITywpasl -AML6JnUJLMUdspeQPRJGdyf/hQKBgQDrTlkqxZ0tnME6yqpHzS/WAYGMIpxWzPWH -sY6E5lINvsMuy5OjPtE7Qgl/fbu/wjdt0VUw+BeuHbgJP55S1vmfrCSvRwHSumXq -7Oyx9fQfz4bBiV0BPbXSE8OlKd+mFsQUq2BZA12NFfWpw6R1BG6wkyxB/qN3psUf -SpwCPPtbWQKBgGzb5todN2WGcEzcawMJfY1/qDKsrn8dWx2nsSL9YCGCiiPgfSOm -86+YZFAT2gI6A3tUBtQVeYu3XiVlzpf4QZMALF+F8TT3oYq0j4Ss3a20ynI93ji3 -Wt20mp8cToWDCksH2+EA/mZYmJhl9Gs9WRJgr0azaw/ia4R15EAmtL6NAoGBANN1 -Wj5Mzn5iA2ghHrtd0qJAQjo8FEEQ+YlS8B/Ql2aQyvivq+KlHgRhyQVQabve/k7r -MuLJ4QXw4SU2jcImPLfYTDNT3XiRV+uxtNgMjrFTnucn2XO3EEFUz5oKslig1t84 -T2JO2vBiDp3cGluqewqlh71a524sabNAKkj8omopAoGBAOPfjxvmQprID0pID9xE -Qe4+ll+BAUQN1W9wwOazwMSON2WOOm+2IL7RPgyNgFWeMzBM0CpBq8Pve+G3N+xr -RrOmsAJdofK3xM0UCnoWLazNuoJZvuzyRyqppIiG2DT6aBVpZ9Rgcy7ufdQmj6tU -YgvO+lsMalyGDMSP21hWtgxN +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDpEsvHHd1/cufp +MbeI5GIk2xB9mxFrTReAaHNt1MY4nS9fliOreKBB4ZV3doKoASK9oOaTQyTqZbIf +mXU4Ofvk91hQUxVuWY1oJnWxjqVze4mpY8TPoBliiOQuuG6a0idQPrL03erYKWWB +P8HXJvgd+zZaxMa1mOZg6p0q/SBpPDSnjpSPRSsTjYY5WOushB+iarXpfBQOHWSp +8jG6hvgn9kc3ikewW0i+HcXdkPY4Y+JgvI8bI/cLWyxScKip8wzZNQ70sZ5aq5G8 +Iuhh76MDlDyVsNTU2PGtGV9L4vJ4KCf3q9ZaaH9MdWdoKJKPg7rylkAGyCYeGD52 +ILy37+xtAgMBAAECggEBALcd/2BnhHj3rGE4oHw7Ayj1vBUTWjI1lXtblST7gOlG +BcxRx6CW20InnCkJwUmB6uuVmNKmdEB8ud+9z6znUZLpCkfBnxrd3Q2w+dMQwcjj +jJq7OgvuO0OBTdWvLacR6bVQLYXZPQOsMoXDs+X4RgQ4NNJHu989S/NlISp/SZGj +Ld7BMd7JzWcd0YW2VEUdb9Sebgt4/ZoC/4iAsJCTche7zKkTPYXz6JjgO9ggNLUj +AEFMrdvC5VZbth5E45Or8+HUPw9Zdq0ypI8WaKeWVmOQ1vF3M4fOW5MhEwJ9DArD +h3D5xooK5cicSLTcf/RkCKBt1bTB3ra68+MpB3QcKwECgYEA9IH0iu30dRYDl14t +qKsVHrClxGfF4WlvwYYnMoFudMWPT77HMBLYfkh6zRcXTAIfUDGUaGMKbAza4GTM +xFqaBzi7+cWFUO0KXx0JpsMVswim5H4Nowl9chxszXhsE0KsPoDrEWi8q7Cezmpf +qPyNWhoNH0TCi1+JCOB2mZbDE80CgYEA9AdCHM033uJYwiRJHN1KL/9kDw9N9n0t +0s/yXl4n4gCoCUGxyYqhdFVa9MGaRB0ScCKz5NmbMW9lfvquabs/ujwmi7mRCq7X +/RYHuhaUBgaa1sDeQ9PCqDeqPfutIcXZNmVGoi6MmChufZ7i5CPw+UzOBuvY3EzQ +uaLYaXxC2yECgYEAqbRVNeRIysd3wKTWTf5Ij1+N/o8EMhrLPWfOZuic8KF6gZ/p +gn1iE+xch2KigdfrQe7XkchEIJCfjiWIVjSIOcDv/0FP8cZEb5Wo8DzxH60+oNK4 +xlM85WnDPq15Xbjl8g/ql8+5O/U/lAcS1ChON55jQzOnT7MEWsvopVuVlHUCgYBL +Oe2UiWLez7IC70KmpkUXdMh4qAQlp0M4CKizlJ0A26jxaZIQCi9peGKYrxIKS1gC +v3/rXXWtEKUeNtMLfxDPLXnIewUOYQXuXBs3r3wkO2cCKuSZzi7kwnw1rV8a2S3z +ydazalwnD9e33AIfyECh6Z5dndj1WKzDfULvicHmYQKBgQDhKXZf5yoxqpvAlycx +xG6KNmCaV21wTXqu4xdNWV6hziejInNiJUX98H/Nfj0Nt/yKSAMo18K1xYs8ZmWy +ADho8EcKYkko19IgO6XDMrFNrNr+y9GoIqfVrOKJPwu20hCV0z5fn7YeezRI+TW0 +uI6IO5dYe482sNCxm5zWsvH3dw== -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,24 +1,24 @@ -----BEGIN CERTIFICATE----- -MIID8TCCAtmgAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MSQwIgYDVQQDExtJbnRl +MIID8TCCAtmgAwIBAgIBATANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh -bXBsZS5vcmcxGTAXBgNVBAoTEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsTEVNl -cnZlciBPcGVyYXRpb25zMB4XDTE0MDQwODAxMjUzN1oXDTM0MDQwMzAxMjUzN1ow +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1N1oXDTQzMDQwNzIyNDE1N1ow HjEcMBoGA1UEAwwTbWFzdGVyMS5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBANtT0Se0OBG+bU3ZbZ2IxiSKNs7ZxDBoyXVeVGvOvEQW -56TkHnYdoJ3bn3zLctAoWMggv4DxO0nncmVJYbFoeZo9n7viUQdsO8+hTWVJCjov -uZYNO88Q5NE4zP/Pi9IWigOzjNMl959ItGI0Sr/aPZUpsc/V6eEpyY0eREGG6Ixa -eeO2z/kU4mqO9CK4VzNxfZQqAi0kJEEp2gQ8Ax0gCXee4gbBF7zvyi6467Q3hJTf -413cL0jMIPHbNiyXdLlzjtmkYDL9mjnXbL1W339twBgPzs/ZjDqR4IBK4Fzqakoz -WvWbp1aTYkRqSBiNRHtiQleCXG7JU6FDeF/wzXXWkWECAwEAAaOB2TCB1jBbBgNV +BQADggEPADCCAQoCggEBAJnqPmMTC+OK9BL8P3+IcSZg5PBFwUU7Z0clkn42nwJP +WyD9C1/EbQNIsEiY2m0dy0ygr+35naIxmEWJ0nsUjhgMpS3QngXjHNKNlR1Zi2Vy +/LYdQ1orRH/BKytuc9lTiau9SqsM3qVlp16CrKc3rzyU3cxN2aTYpo2vrHcF9KNV +aFrmE+n3U8GfxX0XossPkZeyd6RYoaSZqLtTw9CfYqmEOJ0u8kicTmAK+1mK0sAO +prBjSiBdwVVZHnmv8VOy1GcfB21N9uEafHmzbn160E4ARs+HLtVc3uPWsC0i7jaq +eDUajpcRmxYSZ/htN06rEwywplJRJGjMipM2fsMU8L0CAwEAAaOB2TCB1jBbBgNV HSMEVDBSoU2kSzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIg T3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMB Af8EAjAAMAsGA1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH AwIwPQYDVR0RBDYwNIITbWFzdGVyMS5leGFtcGxlLm9yZ4IHbWFzdGVyMYIGcHVw -cGV0ggxwdXBwZXRtYXN0ZXIwDQYJKoZIhvcNAQEFBQADggEBAFUas+1NvtqTsT8X -CHiwL/njj7at7V6BsF5yw/MnJ2oEwkJpfsp7J3aB/R1s5bxjtxOJ5fVzED3L0uIf -we29p16rdSeINn9D/LShF7SUFIB3GokT/L5gHgYPLGH4itmz+GKul6qBdt0bOydM -1CqfKTmMEvH0sicEDRFIxji+dfrS6lPhdDHkdKGJeEWpNuATYmw24NYOIpO+4Bv7 -oVXn5hoZp5VzbokCzVha1hlsUeG+wp3GnOoN2aaAm3LZNqKLhm5dKoNeRtECFEOu -+GViwgc9RG4GN4jNDGU03+z+SMozlt3cc+osIxeOKExiK2dfhJwA9Uj1uvYYnSuy -/hHeAt4= +cGV0ggxwdXBwZXRtYXN0ZXIwDQYJKoZIhvcNAQELBQADggEBAEonitEmfeJx6Onu +Qjo660ZVkd0t/e60aS5o2QnMD6Bc8sljovRh36/PDU+I3pHSkCCo53npsNAFDdL7 +pOv+y7rLNeNHTQ3/LglWQwMwDasJM0HcV35UatYf9q4ooHSXDOtZzcu6t20/KpW5 +2IQtr6f+JFlNa388/aTURJzXqeMHTTPEQMRicp0V/svHogWYEZiKCfDlW7k3va8V +eBd7m6LI8iBc0V9b2ieRkA16e1sZ2A48iaKTiErTZl3xEc25/S+HOpp/OKEdFr1+ +CxJ5eNKi2i8o3nyRcGz7wa5QgU7esL3V3x+h8ycCtHgAFUqGveqwDSHbBeD5+85y +Dj+STIQ= -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYzCCAUsCAQAwHjEcMBoGA1UEAwwTbWFzdGVyMS5leGFtcGxlLm9yZzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnqPmMTC+OK9BL8P3+IcSZg5PBF +wUU7Z0clkn42nwJPWyD9C1/EbQNIsEiY2m0dy0ygr+35naIxmEWJ0nsUjhgMpS3Q +ngXjHNKNlR1Zi2Vy/LYdQ1orRH/BKytuc9lTiau9SqsM3qVlp16CrKc3rzyU3cxN +2aTYpo2vrHcF9KNVaFrmE+n3U8GfxX0XossPkZeyd6RYoaSZqLtTw9CfYqmEOJ0u +8kicTmAK+1mK0sAOprBjSiBdwVVZHnmv8VOy1GcfB21N9uEafHmzbn160E4ARs+H +LtVc3uPWsC0i7jaqeDUajpcRmxYSZ/htN06rEwywplJRJGjMipM2fsMU8L0CAwEA +AaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBfmS58AudJWE8dqHMn6y2htfFZRjQMvcUg +floAn/Y1sKDKwWF2XStb4GHq2fBEUxGHJ7euXriCf+/3tWddeowH03h1Ek7I5SHb +6xh59OIMAeMpwxyswHtVtVAmxCqqrBchMjLK59EbIDzIMo2c2g5cX0LxeJD2Wl5X ++oE0NPgYINfY9LyAfRY3IAlo8qESC5DBROgmys4cz9yU+FfSslC4+fkmgnEENsg7 +S636TqO1QeymRnOKp7NQu7xSIJXFw/nwlR7+Llg94dYPk76BQWg8RCZgX3xsiUCC +ps99dYsIYfuapICLlzqOLbRhH5GMzW2ms4/shAawK1E2O2CCUhej +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master1.example.org.issued_by.master-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDbU9EntDgRvm1N -2W2diMYkijbO2cQwaMl1XlRrzrxEFuek5B52HaCd2598y3LQKFjIIL+A8TtJ53Jl -SWGxaHmaPZ+74lEHbDvPoU1lSQo6L7mWDTvPEOTROMz/z4vSFooDs4zTJfefSLRi -NEq/2j2VKbHP1enhKcmNHkRBhuiMWnnjts/5FOJqjvQiuFczcX2UKgItJCRBKdoE -PAMdIAl3nuIGwRe878ouuOu0N4SU3+Nd3C9IzCDx2zYsl3S5c47ZpGAy/Zo512y9 -Vt9/bcAYD87P2Yw6keCASuBc6mpKM1r1m6dWk2JEakgYjUR7YkJXglxuyVOhQ3hf -8M111pFhAgMBAAECggEBAJxG78wTnLP/9NA4seNC9rRIi17+Sc2YjJuFmC+tAfae -P3X9WTseRzjTqaN5L5jkdsY6l1mgCXfSY2+KRwLrB2KAsFVmoAfi9gcuzv/xeEkX -gmxJh6k2R2RQzbkkwGL0zmhuwlQdRICJhIZI7k4fiivDpsAJkvluFf/oZgguwXpn -F62e7nM2rV4ApH8wN9wixFeAONv9GxiTxjLCYWIMeDP9ETnIsMPTuzpbjHn1cXDt -kobmRma93jUzJK2wtsyrvsj7hvYPV+EzHhO8N+VK7FfZ90FBbWQDM+nrxOePVmsY -t6KYpVh+B02UtEuVwg+qc7E2bhSxQZzhuuTy27DszWUCgYEA7iIDJtxR8rcNAr5p -nMrnJ/ZFtzUMxk1K77hPWN3dLhT5nr70WDUmg2xaHyS8VDk9sIyERCOt+fyngj9Q -AWeukD7xwpLzZ0oivK2btT9OG9OCNZYeu4NoWX3ocI2GoHsV3TdLVkSKsWv9Z7EQ -EXBkFAGgrclMpWiyGw6sbuKBq68CgYEA68iYUhq13+oTrX2nMBjkah5YsBpInNng -B4IOuvcfUf6gsewLpbkcpg4UfUqQxoGsla4mK/5Gd9uIWRENsBZB/ZYKqjQW0bfo -kyTXXJy5Phkh/oo4bgSVWGIIiTI3F6tuXu6X75HTiXghm3m/87X3p8AhZk8MURQW -dLePVDAih+8CgYEAvylqok2HM3Ki7SryGT4A5mGagYICqUXu/BVXDR29qnqIEFl2 -SUERk7rtdcbFsE7rKMkEfLavuNiLl9E/ZoFW7tC4vtu8rZQj4pbzQkJ5b3kRM/c4 -4IqSwBSE/aV/B2EHojf7MFuBgwAPwqevIHC6xhywYhIQh1BOec4Dulf2hF0CgYB9 -2R+UEzWoQiQmob6u6VphWbk0pZLERXZSC5UZLfXFqgbTcI328orcBv/gr//+NBCO -A9nT+XBbYQ2xnGyV5Ats8rzWg976KRM2Fp/siqpE/t0qI1RjRIcCGbE8qVTGiXXr -raXi9Q7XfQtTFPTje+in3OD23pJQZExoF+GkqdyEeQKBgEBm6ZzuXYn9hkoBySK8 -O2sFOUJLE3ptdEdzBHGu1oZNgrTIVIwSykmMwzRtLdJz12gvHs5+hqdvROzZGdHy -HAXsEzv8s5RTr1cUGUcCueBiFeiOfvIu6YsFl08WpSIya4bGgOLNRojxvqcfpjn0 -nyYXiflNy9ffvLvyXKdq0nyW +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCZ6j5jEwvjivQS +/D9/iHEmYOTwRcFFO2dHJZJ+Np8CT1sg/QtfxG0DSLBImNptHctMoK/t+Z2iMZhF +idJ7FI4YDKUt0J4F4xzSjZUdWYtlcvy2HUNaK0R/wSsrbnPZU4mrvUqrDN6lZade +gqynN688lN3MTdmk2KaNr6x3BfSjVWha5hPp91PBn8V9F6LLD5GXsnekWKGkmai7 +U8PQn2KphDidLvJInE5gCvtZitLADqawY0ogXcFVWR55r/FTstRnHwdtTfbhGnx5 +s259etBOAEbPhy7VXN7j1rAtIu42qng1Go6XEZsWEmf4bTdOqxMMsKZSUSRozIqT +Nn7DFPC9AgMBAAECggEAVtAlsjIbAZPtiQvYMPHfiFscpl1h3s/bt+h8UU4zr9Tc +vKWdlb2wEysbq0bz5JnqgmUb9ilV/RbmIUrUenGkl/IB9O53QzDPn/8VvUE1e9o4 +qeulgqVm9HwbAltBYrJYs5KIHCSdLENC/O7NvV+OKqh41fSPLwi4ILCWoWetjbYN +wnhOC44uUTGJFEeSzhfAxkY8vfwCEtNa/oCApsmxORcmu7M5RU1dzp2qIBe/DOGG +hB3NbfyU3q9iKzcI7+iNOMzzV1TqQMlEYiSwit1W+wFj/0NZCVTWHgK5vnOjhp1N +i+j0ykuEGNXgG3BCewXXltND4WqNJQYXBXdlrppP4QKBgQDG9d7l1Mmi9kjqDfKM +BCw27DwySEiPvHjAii3Ah6J/8ANoge9rj7+2OsxC17LbHYawgYyAvaW8oEanGgE1 +fdMNb/Ujrl2jOE8c8dwZP24MRjJG+ACEFHeQvzR9lPbsIru0h5Bxabn9ZNI03B9t +E9emhKR3HRukD6FaPb9UT55FRQKBgQDGCmTbdnyX5sy/X5l/pQoaZPCaMfGcgBri +EiPEljmoFMyO2UiJV923Ki+/6YaVy1+f/bUgBlN37nKpfwTWSMHiz3OopFYV6fdn +dRt0Pm58NgJEES2eA0qElA791cAjHFBNM9Bf/EJi/yC81dBbPYD41HUSm5zombES +WYHPKpnJGQKBgQCaQrYw4pv34xbDik7darrRVraRkePYthu1xS0WEtPWeERcOipk +7k6JPutyhAUjyK/OmJNUmjYvmuFM3GfFjqodyAl8QcxOKfGFEq1BUiHEGIUQ62aT +Ab92lhlfqSkOCxwYK+e9V2kiY5Jr86PWEcVpUXym8gWlXY0QB8IZ36VqNQKBgHmx +A160BUWWJjRmez0rXfTwF8S4cd4X/ezy9pWjNhSpizUwIVl765rUynij9/Zt/Pmj +/buLB5lHpZ+vlHpURQepMEntiZR3q3YgSNl6T4v16BiJcb2KCi1DiZ7CzQkGKfX2 +o9GHggew/B3tSNX4Chtc+f+QLa/kWSGMbK6vX8ZpAoGACIjRquyI2jQBCJmj3XcT +HlWNB2Pn8VMKC1exDxwTDinhQKoiwIyIRpgHYcLF/POBA22TGC6bfxHjAc3c405j +wRYfluCJGaoYbELGqo/6OgDq5ZRIHRHEShX7m4kuT6qJ4Tf0UV9CgZeLEgwvOxas +7wf6BWMsOPt01Tr2uvOtiFI= -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,24 +1,24 @@ -----BEGIN CERTIFICATE----- -MIID8TCCAtmgAwIBAgIBAjANBgkqhkiG9w0BAQUFADB+MSQwIgYDVQQDExtJbnRl +MIID8TCCAtmgAwIBAgIBAjANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh -bXBsZS5vcmcxGTAXBgNVBAoTEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsTEVNl -cnZlciBPcGVyYXRpb25zMB4XDTE0MDQwODAxMjUzN1oXDTM0MDQwMzAxMjUzN1ow +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow HjEcMBoGA1UEAwwTbWFzdGVyMi5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBALADxMRg/KiG3QDVO9RbJ8NlEF+DovPrc1GmjYlSewEj -3mYf5v1a/TTHZOeB+iA51qFW7HGhDJltBZ/0J71y2A2x0nVbGAW4EkTfryKaCBBl -fwE5fv2Xm6fJB3YAYlPUw+WAZ1HMLrBtqkpIy4xirAy4MCirLBnKxBAjT8kk1f6n -8fV7l3Sz1frtl5ONSaiLsCzDBDfGyp9HDI9X2ABzRIh64SnDCWDq9OZ5UkM3gJmh -vZGT4VhwSJzPJohMPOEgzdKHjW5aFhx4FIHhmNJ0jucEXjNtF270lHjBqjfnsrKF -xHah2dvhkr6PjAOUR4SM1hCXSru5Msa2huBD0Q5vh0sCAwEAAaOB2TCB1jBbBgNV +BQADggEPADCCAQoCggEBALPe3Hu82akfIiNLnG8vYZVK1+T298lkpWepwGUvdWWL +FO51J/+B9av9dERwA7EQMB2EsqiDRLw90Mb76T5MBqNmgNn4cwjP/Ur6+jCAf7LD +MO9y9tFd+CFcG4gi+mKAUS1o9Yq6wcc3lnAIHZYaYIFsLwE8m0L1IRGbHwrVCwRR +Wm61BqBVVC9s4vGopPJ930IH+D1kNXc5veNwatiWwdK4oXt3ti6NoTbpeAwLWYEo +/62Tw9WJl56TGgLrEamA0vmwZqRQiVPiCcsFGSaSagI6LfsznkjWcdkMWnZBJtky +Ijc6kgTv514oJ9DgorO8cmQ1Nh/8sYWWuu38a/Ec4MkCAwEAAaOB2TCB1jBbBgNV HSMEVDBSoU2kSzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIg T3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMB Af8EAjAAMAsGA1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH AwIwPQYDVR0RBDYwNIITbWFzdGVyMi5leGFtcGxlLm9yZ4IHbWFzdGVyMoIGcHVw -cGV0ggxwdXBwZXRtYXN0ZXIwDQYJKoZIhvcNAQEFBQADggEBAB7pmCkatEPj2UTL -OO0/xcYXog3NO+90ZEKkUoQpe+cVEkOdj1hZQ8OLK7sennVqww8Gc6vRjLve7t8W -ApJevYCcLAx/fNdIMpya+8ZPn+ybm1JY6cAwUauJ4BdrqhQM1zdKwbleSCcXW2RQ -TtsFBtcnlNhd+f3lcEIjM9oI/+UlituQ0dZRiWTYbswFer06DwJWQywiWMr0oZip -P+fzwwewb4ri5L3i683ZIpl9MuShEqpM0SPNl+XdmnZn0jDNK4bxOezt7qwZl6T4 -JbRshdQ3ClWa2CcAdJgawfd9VRZC8GWQ5PZYNr5rWwLtrDBX2Uf102O67TXbNOnn -yg4Ci+8= +cGV0ggxwdXBwZXRtYXN0ZXIwDQYJKoZIhvcNAQELBQADggEBAFIbMGPuJ6FvHeiU +h0BQ9DaPj1kGpqW7SpR9KcfboQDodL7yrkSGdOJ+64zKZr7r388HFpaS/xN+ilay +VRXG1l99O7pOKmvr7t0V9CqWMvuALYV9CLmGIVKzrc2X36nngzaNIHlgiJ6PsbAh +w3V5+z+xUIiHPRU+KHzU0ka5Hn/zTkqkWm0Ci7fTyZAfUMD8gPo0cM79NWlCulej +bAkD3H/KRxBAVo6P0e5xB1bWlS+4LDd8Y6G1l29PAc/FrSgTaW7zz0A9Rf8UMQM+ +Ye/f609U/IIr3W+Q64AU50xlY+ufSuP7KeZanCi0FAS0HuuNdqG4MQWyHXppTgkg +HwYbNEo= -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.csr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.csr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.csr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.csr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICYzCCAUsCAQAwHjEcMBoGA1UEAwwTbWFzdGVyMi5leGFtcGxlLm9yZzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPe3Hu82akfIiNLnG8vYZVK1+T2 +98lkpWepwGUvdWWLFO51J/+B9av9dERwA7EQMB2EsqiDRLw90Mb76T5MBqNmgNn4 +cwjP/Ur6+jCAf7LDMO9y9tFd+CFcG4gi+mKAUS1o9Yq6wcc3lnAIHZYaYIFsLwE8 +m0L1IRGbHwrVCwRRWm61BqBVVC9s4vGopPJ930IH+D1kNXc5veNwatiWwdK4oXt3 +ti6NoTbpeAwLWYEo/62Tw9WJl56TGgLrEamA0vmwZqRQiVPiCcsFGSaSagI6Lfsz +nkjWcdkMWnZBJtkyIjc6kgTv514oJ9DgorO8cmQ1Nh/8sYWWuu38a/Ec4MkCAwEA +AaAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+giJgUBjZV/MKb0KQ4QWLXPa7zisiNtk+ +nRHHeqynRE9u4UAxWCKp9onBeEKnsqLHcrEQl4jvWBGwmMPr47fgFOVqZjJC1om5 +O4pyZctG4xPrCFiH6DspoSiYAoyMt/SCnroMaYKUuMYyUsMCf380HLmsd3IU7Vt/ +nMXKLTofaL1J3K3ymJawRHPFXUac4OqpeHVvdNacxi72Vagyzpctbq3Gz8K5Qose +rUPYQY5mr8JsNC5t7D4kgINaWfocAuf3LZ5gc88Qk7RJNZSSra7qg816aOuA97SF +yFBxOXCv7mvoy3xXSjgYATMoa5tg31Ib0suSUr4nq01V4wl3fF8y +-----END CERTIFICATE REQUEST----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/leaves/master2.example.org.issued_by.master-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwA8TEYPyoht0A -1TvUWyfDZRBfg6Lz63NRpo2JUnsBI95mH+b9Wv00x2TngfogOdahVuxxoQyZbQWf -9Ce9ctgNsdJ1WxgFuBJE368imggQZX8BOX79l5unyQd2AGJT1MPlgGdRzC6wbapK -SMuMYqwMuDAoqywZysQQI0/JJNX+p/H1e5d0s9X67ZeTjUmoi7AswwQ3xsqfRwyP -V9gAc0SIeuEpwwlg6vTmeVJDN4CZob2Rk+FYcEiczyaITDzhIM3Sh41uWhYceBSB -4ZjSdI7nBF4zbRdu9JR4wao357KyhcR2odnb4ZK+j4wDlEeEjNYQl0q7uTLGtobg -Q9EOb4dLAgMBAAECggEAOUlgt1GhqfAK6gR8robkkDHKfGvJk33kUITWuAUCR8K2 -1aI4eektVAo5iyfAktCATI6P8gOqqBzVodPDQ2Oncf2YM93sGaBpQcVTqP1c9oky -JuHhtQOtA5ToOr2xQvuSrpV2CydbYsyuifLbXNuYh6NC+VmMxvA5k88KXrsAcxsH -wOl0DxTw0/enPCdxK1HzV37Vgq0yaJtkm1+NkvYQREcbZz0TpnUX3qSaFRv+XI3t -lHbLgCZfW5syUgibZKYmc2KHJ7WUpnjc7joYooc+cjQ/eJ3skcPu0JqaJMH3Lzmf -4dfgUJ9CIyyneuxoB4kxDPKYnsJ7l8ttKd4tR155UQKBgQDgMzXUwfmskGtgF7mq -+xcSZrQ4mNp4qcyScs1ALX9M41Tv0r3HzottvXjtt97Nfw7CyWIUsk2Z11xqp3S4 -NQv2t2GK0O8Axzte5nt2pG4E8phigj8i8cqxDyeRqJEJzaZ7d0ycDFcMsY9yBuzB -beu+BZnbfxLaGTAdouTmcqp9/QKBgQDI+u5NoBy/C2Zn7PK8oAYkoiVlFSg4cfz+ -rM1P5xKOslVzMjFpkU7djYYJdBwk/rnvo7p0BAooIm6bK6BdUKHx8qR0mxroD64c -O+ZTw5gobyUIMAvtb3/c4IYFE5kOCBN0vRXkxlpQoyJlH61eh+mN4CvpNSvgAfU/ -uMIJxVK45wKBgAo1SrYtPtkEml79tY57cEl+M6aADzJ/SLlCUOYjLDCsCSoCRnHa -ja6peeAZByQELP/he33FwJLIo4SXq1DX2XuaTe6sJLW8XI+tnuGACyJeiWGy3H0S -1U/KiNqw+imgG3xzqiFQzoXdb9KZTh5giuNUDfVYi9syWmZ/DD0R64FhAoGBAIsg -6obiKAQ4qBOZfBAH1zjU5n4rP0ST5EeI6DhABiCyY2TzgjFKXgYRSpCQyErz61Nx -TEmz4jQWYesZMFJo1zLefeoa1W2ALU9uR+wEoviauFAKsrcTULhe1Wf+QR3tiUpu -+xD8HtljlRPb8fBWtd95J+yw2kagFF6gT/LjnnhRAoGAW6kgmk82hUCIqX/uEqW7 -DEsLy9mbbFuWjoqt2ma+6Urx66muX+SMQj5pwd6JnRRSexWVAxxNuKuYUq1ZOhom -e4+/+VfkiFzPzVCA0VQ1YRNHt26Uxvr3ovZ5Y50u4FH2hypNSHLMFZV6T2EWt4Nd -b5Lr8vvMoJlCVp+O3AvF154= +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCz3tx7vNmpHyIj +S5xvL2GVStfk9vfJZKVnqcBlL3VlixTudSf/gfWr/XREcAOxEDAdhLKog0S8PdDG +++k+TAajZoDZ+HMIz/1K+vowgH+ywzDvcvbRXfghXBuIIvpigFEtaPWKusHHN5Zw +CB2WGmCBbC8BPJtC9SERmx8K1QsEUVputQagVVQvbOLxqKTyfd9CB/g9ZDV3Ob3j +cGrYlsHSuKF7d7YujaE26XgMC1mBKP+tk8PViZeekxoC6xGpgNL5sGakUIlT4gnL +BRkmkmoCOi37M55I1nHZDFp2QSbZMiI3OpIE7+deKCfQ4KKzvHJkNTYf/LGFlrrt +/GvxHODJAgMBAAECggEATFC8P4vK1eStuUf1YX866frNSwo1667WNvtg9A2Ew8aU +5O+0PsZHE+f/kqHsDw4NPBj3LGQ1n6Cimip7tcDP+DgfZ1vwgsasc0m1KSr8Az6p +3o5qhj5WbuEDlWkxzuUejCPCOlAus3W0UGhBFVSvqXnbH601Ot2CYFPugcwOVo/v +H2sc/EldV9QVXl1SDVQWT3EjZ4ONz7txqrUael1GhUlx8jSiL8MHKrDjYxdyXfV7 +kUCoDe84wZWD+Xpi/wR07VFWnC2QSzPz0YsXMsmdyQKwHP6Atf3+CSJVxop1EZbm +n0D7jXzJmyeFjXkt6ONOKQynbzQ6yx+XbNlBYybNAQKBgQDoftA6EbdmK9wOMaRd +xRjYgh3oAoPIqT6QjKrjQ2h/D7QfBLLeJ5VlUg105PPfAXhaUxMAYWzOfYmBuntG +6JB3JsnjTiaS2XiJIok9y+f7ItItDF/59DBYqnx5v20SzTy0n9w8H5YL85xdQ4Gs +BfIBu+/elKh6L7taA1/Gf3Ky0QKBgQDGDhIqakjFjIdymmRa0X4bzOaYCllLy9C0 +AysMsoPuAA7Ytwtc97Gg/EyNuzgOfex5nX0M5hQ3lT3DhtFN73ltgK7CHKhY6JhR +c3tjws3IBilxfw1oOaFK0FHC6dTDYK201LTxuSftuDP0C+/rMDgnvMaZTKc5KgxV +oANtVb+ceQKBgBZdHGp5icqqLWCwJb9fViR9X6MRmtuSbBI7dbSmp0aRnCHfsdTs +udIlMmYsH8kzuv/1Pb6roGu8Z52y8mi/tiW/9WuH+O3nPVDzoX/wPUBK+SycJB9G +/d+Jhq+vWi6Uz2IQfn7KCwTbSg8ujyqc58qC6W/fnNUkAkGGUZAmckIBAoGAGA1n +aV1E/zDE1kcnvtvIO+oTaw1+sLTM4L+tv7oCSaoaI3l/WMf/+3QIfAHrmd7zFAXr +RODAPcvTF2bNDnDxxzmkf5BpZXrB3MpwhroGsa8F43GxmCG/k81e0JQDMcMQ9XAL +lXuN/NGtBYtZlK9jcnf7imyDtm6D1vY61FFglskCgYB5VL93u6Y4feGCRUNmFKvF +QJv+5D4yaQyRUuRtAX1YB7O+gLBLAgwNH9v/gzW43/nLEkqzEQuNSx+bgaLlYjIr +5ZhYZ8kFBxkT2MAjVxx9ZPmJ5DAmKMiAO1dFTboEdiCyoM/xHJW8J4zfej7U4F/Y +z66hiTF02DptvhkA+XVJyw== -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crl puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crl --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crl 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crl 2024-01-15 23:29:55.000000000 +0000 @@ -1,12 +1,12 @@ -----BEGIN X509 CRL----- -MIIB2jCBwzANBgkqhkiG9w0BAQUFADB+MSQwIgYDVQQDExtJbnRlcm1lZGlhdGUg +MIIB2jCBwzANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRlcm1lZGlhdGUg Q0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5vcmcx -GTAXBgNVBAoTEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsTEVNlcnZlciBPcGVy -YXRpb25zFw0xNDA0MDgwMTI1MzlaFw0zNDA0MDMwMTI1MzlaMBQwEgIBARcNMTQw -NDA4MDEyNTM5WjANBgkqhkiG9w0BAQUFAAOCAQEAYMRAzRxGX0SaCUYZKNn25JyK -uNoT91hdmUuSYnIiHkNxft/8x6EKSllrc1nzn0/sBBfzZ1VAK6tZTPHhWZ3m/JDy -B40ovaz/jgQEowKf+LL8lI8wVqa3ycwAclpACmvMpwhVIWTGOSI5kDA3+rI24J+k -z8mlTWJ56JcdWlbniZ9TPR73LixUvBq8t1dZGsj8Xez+BHcgcdT79M7HMuGgXws1 -JHbHc4N8s5Ursp6np6o56YRSU/24kH/Dzp+4ddSGV+nTEX8y/tQsIgSLlZ9fJoQi -HSDe5/yTzhYVvOyW7ZLDvuy409kyNuESsur3iTKLPLQNFQwMsM++yt2nKWOzcQ== +GTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNlcnZlciBPcGVy +YXRpb25zFw0yMzA0MTIyMjQxNThaFw00MzA0MDcyMjQxNThaMBQwEgIBARcNMjMw +NDEyMjI0MTU4WjANBgkqhkiG9w0BAQsFAAOCAQEApEjVOh+q7CWOFlvAstOfYrp9 +SsgJp9k3Lcw2IHNqKxcrpszvLRjjCoUuc/qF6SIOidT1wmG6wXfSm5mlXHZ67KEm +tJh3OgAQhOlbFrOOk0T7+C3qWKEnCaOzuYhH757VwCuU5n9zZQBo9ucHZAPC+Je5 +oTrGA9NHW65/HNFYKNwpHsdLOKmnTGnSRqWhf3CmS9tzQU3tXWz4g1iQoCMFjkYD +h9BoKm9/b989aI2T+dYPdzcWccfONt7cboPZzgYtb0iuq7aXXFqLDJl9wkxtyF+I +hIkzb0F/Rp6D4Wt0rRw5AOMlRObWxM1pKu/jUv3O/dcpMUonp6oDn3e1NJBFFg== -----END X509 CRL----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,23 +1,23 @@ -----BEGIN CERTIFICATE----- -MIID3zCCAsegAwIBAgIBAjANBgkqhkiG9w0BAQUFADBJMRAwDgYDVQQDDAdSb290 +MIID6zCCAtOgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBJMRAwDgYDVQQDDAdSb290 IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBs -ZSBPcmcsIExMQzAeFw0xNDA0MDgwMTI1MzdaFw0zNDA0MDMwMTI1MzdaMH4xJDAi -BgNVBAMTG0ludGVybWVkaWF0ZSBDQSAobWFzdGVyLWNhKTEfMB0GCSqGSIb3DQEJ -ARYQdGVzdEBleGFtcGxlLm9yZzEZMBcGA1UEChMQRXhhbXBsZSBPcmcsIExMQzEa -MBgGA1UECxMRU2VydmVyIE9wZXJhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDTgKLGBkExFRXrQJn/lHE4XHkN2nXwJpS+y8bWqHiBdq5eZ8D2 -UAILOBaALeQN/1d1J4yrh6w/YK+gRtCLn+CslR+9NW4AgShALi+r26DK9ZRk4F7V -Dk4yEjNpmTyVRyP8w7iZwasZdyK04xAhj+yEInz29SLxmh1jddts/rjqLMZW/s0S -T+E9XSEDYNVprC5VuYutUuHKah7AYSp07FHNsqDg+y+vCRezSqbrHrGpTwMupVmD -2ObsSJntghsLzPwjSGhbo6e8C/TDwrPtm6az9TPKbsUrqjdvyZcSfc5Q6OgExNhg -zWQkk5PqFOESsQSBfOOn2eqfqBXHUnH9PCNTAgMBAAGjgZwwgZkweQYDVR0jBHIw -cIAUFq+AJeP66ki/kTNmAf1R7yRnTGOhTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0Ex -GjAYBgNVBAsMEVNlcnZlciBPcGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9y -ZywgTExDggkAsbkEcvsRJ+MwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYw -DQYJKoZIhvcNAQEFBQADggEBAC4keJ+jeGh7/EWwsCKollYW7H4aSjPu/Ufe38hH -pEER9FyCqJ0jo+MabOx8l1F5ySNWngB0qbJuA/kiV2gJ1bQ+mE2TN88x6Sz12eol -ifhFU0PazGdpNQRhpQxbwJ7tFC3Z8WrHEcVqP9iicNWqSI/QkqXsCk4Zyezpx28W -sqHylf1CiBOU45FJdDXRg80mk6WOpNZR8HIUdqQLQDXz0FfXeFKmVteatmc/yrGG -5iHzMkH4Vz5laBjin9s1p+O8Z7+cWtJNWfXaULAEecZQ6CZ3V1OVOjhsrL28iF7C -kx01rSrsxaFclDalJVmKmO2spHvNmQTlWD6jm5d94WaRyXU= +ZSBPcmcsIExMQzAeFw0yMzA0MTIyMjQxNTdaFw00MzA0MDcyMjQxNTdaMH4xJDAi +BgNVBAMMG0ludGVybWVkaWF0ZSBDQSAobWFzdGVyLWNhKTEfMB0GCSqGSIb3DQEJ +ARYQdGVzdEBleGFtcGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEa +MBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDAEPlZmffi8ZVgLS29BkwS1xSvWa/LAzL0oSN99sRcEXf5Aq94 +n9uEq7XkooaM+/gfUBI1KGJBLwbKKtXc0xL0i9JGjMcFPMnlU5wCcgzK5KArB5OZ +RV5MM06j86+/wDYDNojYxb1lvePJzv3qdKwkEVtIWRfA29FaMoEsqWWZvEsW5fb2 +hE16JEsTJmHDdPu/AlvwXTmy4lkitS8quTMLkpedtz/Sp5wEuQFmgEmeHkZzRuD0 +rpQe+xbDTSsnlib30Mjqi/dUgolseJqCX0RZ8rdxTARUXn+3aYwnZtS18Y12aj5h +igBzVBZvxETE++MluDnZKATH2Tupejzx0Ts9AgMBAAGjgagwgaUwgYQGA1UdIwR9 +MHuAFPWQhkterSX1ED/ePsbUn0I/h6qvoU2kSzBJMRAwDgYDVQQDDAdSb290IENB +MRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBP +cmcsIExMQ4IULxTl39bugVS0Jwy4ZWpV3IIWTjcwDwYDVR0TAQH/BAUwAwEB/zAL +BgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAKqGMvKEylycCdwVe5idgQhF +u0L81gLr4lcAo6AxCDQXbijD2G1n3SZFl6L/kbUf+S/n3Gk23uE6/Ml5EZYh4Z1V +faNQeH2FHJmsLlPluNAlyCnL5x4KzExM4kRa/UKAf1nOg1ulkRzAAD5WofWipU9k +W+x29VO1eibL0rsJehiASj5SqW9Vm5ZFz4vHeFxBPDNkyAqjPGXc8yZELjrWb0id +nIP2nMN7PNZlWivwBzUwszJU0AxpNYDGbFBlI3ImpUxC5JvpF48ST0V7JzX4HWl7 +IqJqxuDCIkFyiX6eU58n1MxXMsfzCZwR/F3iFOxHDmZjpRDqZAa77WkNYI6QVXA= -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/ca-master-ca.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDTgKLGBkExFRXr -QJn/lHE4XHkN2nXwJpS+y8bWqHiBdq5eZ8D2UAILOBaALeQN/1d1J4yrh6w/YK+g -RtCLn+CslR+9NW4AgShALi+r26DK9ZRk4F7VDk4yEjNpmTyVRyP8w7iZwasZdyK0 -4xAhj+yEInz29SLxmh1jddts/rjqLMZW/s0ST+E9XSEDYNVprC5VuYutUuHKah7A -YSp07FHNsqDg+y+vCRezSqbrHrGpTwMupVmD2ObsSJntghsLzPwjSGhbo6e8C/TD -wrPtm6az9TPKbsUrqjdvyZcSfc5Q6OgExNhgzWQkk5PqFOESsQSBfOOn2eqfqBXH -UnH9PCNTAgMBAAECggEAOduOq606WDr0bA6YEhqBfnGNjASfDrr/JdKI6l/b106o -FJs9ZiQs6vTE8vuaz1lyi1HuYV4T+KJyaN32Xru1soB41kGvfePXJvd7gv/p1Jup -pI7cYlwvd/MSrWiF8jAr4BGKhkU8kRLOI72ZxBSjFuJ4XPZiQMVwFK1BAWgNHcHy -CU13+4sMI7Er425g5pMkv6fWkp6HTRF8WE73e68w1NOvc/6WyiQS9iytNhOR8TUl -vJkFyhcAbg7alsEfQ0Ove/co0yCzbXF85sRYr1ANRTp/y6RoMRXA79qCB4BS2ke6 -UUUU8F+hnmNNVjFAMlojC/yConEyDj8fsh1QoqzNQQKBgQD+5+6cgT8xJpM/Woji -ZogjNoNmbx/G3J00TEhR3vp7Frl+Lvec0OkvQlrof+yfEFDiYpeR8vgjfW9otlbJ -PcnV2ZKRspy4wFqDnP4JQW0hz39uOvZeIbNcIFtI1QaLc29TZ9NpAwiAwD4mm0fr -l/MLVPHtBh0Pn8V6G89lN2cWfwKBgQDUaQQP/s0t/mF6GOAcwF62bxw9T7gMBuu9 -MOE3ehchBjAFdLKn4F5STfxgIsJ3DlMiQdj4EjzM9NsgAA4akRVGDNeypMdTTskr -WbL3188RU2Kfbr07NWce22720mTzNNPf4SZZ7S7BYNYdx3Pg+uFPGvT+L61BXKB+ -3XmktIFRLQKBgEZ3TUIchKCMvmXkHDUiDP5XAoodrBYoRJkBgXiIgIvoY0zbpP3H -zr6OxFzDiGEMfJ+oi6gAF9KPyW2UAnfNSe+BHwCB9Msp+ZuqmZtrZ8TR/LM3P4YU -IOYV18ZRhWaleZ//8rRz7zTQcB3yDTa2oax/RgW1C/GWxFuWNoYPg8L3AoGATtHV -RxY3Dw0pIMSgTSr5OltKsFmnoxbiXWcI+6wbbw67vRqLvQ3eEk6MCyR64vD22mxy -1cL2uttVgK9kjGkv6rmTROAucvR2Y9a6OYvkc7psi1rtb04mwOWObJqwCSueP6Jt -t8ryoaGMcqpzjpWqC8+l/7izYuC7UbhfjkXJAtUCgYAJBcEkCI8GF0+k2qaf2CNS -j8U597tvsrKCFFO7avJWAybZFoXd95/8Aa1SO1Qk+0rm2UVmSRd9YkmmN2UBrxHi -mFw5D92/PI4fLYh9pFOpWs+90CQt1BDtu+nIcGNP3JgW5AGMkv9Boqy+ENdLNJ9t -Ta283HvVvIkM+0Hrd1Zn6g== +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAEPlZmffi8ZVg +LS29BkwS1xSvWa/LAzL0oSN99sRcEXf5Aq94n9uEq7XkooaM+/gfUBI1KGJBLwbK +KtXc0xL0i9JGjMcFPMnlU5wCcgzK5KArB5OZRV5MM06j86+/wDYDNojYxb1lvePJ +zv3qdKwkEVtIWRfA29FaMoEsqWWZvEsW5fb2hE16JEsTJmHDdPu/AlvwXTmy4lki +tS8quTMLkpedtz/Sp5wEuQFmgEmeHkZzRuD0rpQe+xbDTSsnlib30Mjqi/dUgols +eJqCX0RZ8rdxTARUXn+3aYwnZtS18Y12aj5higBzVBZvxETE++MluDnZKATH2Tup +ejzx0Ts9AgMBAAECggEAHM1u10UDq4DXCck8prAfEGQ4seMdPbCJH65aiDVNVRE5 +kJoaflharxag/FxzGQlgODHlyRr5QRUwS3kJAUiV+x5Mtk++OMbY8A8NwkAgMvz+ +F/XW6JFChuLa1DNjE7pbXzf6Zaehs3Q6iLUF1vcQKYiAy+SwbzeHuaMBRBVTKy6S +XGZ8KSCnCHFNtGl+0IKM7ARDl1kLoYc1lnP+81S7TI294GavrYoKr6EA8JETahUB +99k31JFSd7vGSbmJzAPy+bdztsQA6o82WNS/1CtO44Oa1uGPBEuQWSSzoNDFCLcM +neAeTTc30xGY5+LSD6oLS4SZVWjfkvs5df9XofOFgQKBgQD+93bvzWmxZ/0h4V40 +mNchMD4v768Zb/3rh0XSFw9MpxCKmPeMpCbA2VqCc/Hi0620EjIuS6h7kcAmE7Pp +/gRJweyrL3tg32n9iTVKv2sriKK5Q3pL955drHQdjncZiOWJN0toRFjCtbaFRg8J +QcQAsBrkS/T+V3S9IagMrQw0mQKBgQDA2D+bH5ntn/Mc9ZRpwQjSXSPUmHizuvOp +yXJZPT6eJ2ygz7NMi+s6+42TbrNj5TTDo8j1Zp+Yk4cNTLmRCYmOFddDY/exsGC6 +WzYqioD9PMYV3rofkYI54maQhkaURg0GGRI5eoJGiZYriXe1WkSfhwR2UdtFD/Ek +X1+jk1o+RQKBgDWIP7CZsFdPPhae+uiQLpsJRgVKjbkJioapID+8F7pAWXjAJTmi +PYDAIgDB10eIqi/koiSWPgmz0GQuPtgiPUQ/MzG+BmmQfvB8nnPTPO0nAwNi54qL +svR4AOkzwE3cMFVfBrYdo4U+IdRZoU6QQFbkosDMMeQ0+I29Q0tmCeoBAoGBAI1S +7OolsoyuF2A1qNoHmQtxLnzycCJm84KOGrrDY0xL4BPJosCKGY1UJGDTU6v1oUFu +misdVgBevS5F+AgOUTxLTUIZ1L/nXjuz9HIgpn7vNst4OjZAUItTG472mGq5G3pI +jU6KNUa0NdUaHRxOVtQCAocjXemy0Smyq0gZCJ1xAoGADPwqlCmAcjdzm533xFA/ +Tswtk/tJx+Pg3HNmTYHaiMGEHjZLgL0IzRUL63zmP26SlU22GeG3uyGLYE5Pzy+S +nLPzLvMwaeOuZ/jOEPXE42q/HFaKhRhPLQEcNRPZ2to9loSfVcQJ2J+88QieGjjR +DXf3wIoS0S4rMf1fbG9RjD4= -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/01.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/01.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/01.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/01.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIBATANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1N1oXDTQzMDQwNzIyNDE1N1ow +HjEcMBoGA1UEAwwTbWFzdGVyMS5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAJnqPmMTC+OK9BL8P3+IcSZg5PBFwUU7Z0clkn42nwJP +WyD9C1/EbQNIsEiY2m0dy0ygr+35naIxmEWJ0nsUjhgMpS3QngXjHNKNlR1Zi2Vy +/LYdQ1orRH/BKytuc9lTiau9SqsM3qVlp16CrKc3rzyU3cxN2aTYpo2vrHcF9KNV +aFrmE+n3U8GfxX0XossPkZeyd6RYoaSZqLtTw9CfYqmEOJ0u8kicTmAK+1mK0sAO +prBjSiBdwVVZHnmv8VOy1GcfB21N9uEafHmzbn160E4ARs+HLtVc3uPWsC0i7jaq +eDUajpcRmxYSZ/htN06rEwywplJRJGjMipM2fsMU8L0CAwEAAaOB2TCB1jBbBgNV +HSMEVDBSoU2kSzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIg +T3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMB +Af8EAjAAMAsGA1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwPQYDVR0RBDYwNIITbWFzdGVyMS5leGFtcGxlLm9yZ4IHbWFzdGVyMYIGcHVw +cGV0ggxwdXBwZXRtYXN0ZXIwDQYJKoZIhvcNAQELBQADggEBAEonitEmfeJx6Onu +Qjo660ZVkd0t/e60aS5o2QnMD6Bc8sljovRh36/PDU+I3pHSkCCo53npsNAFDdL7 +pOv+y7rLNeNHTQ3/LglWQwMwDasJM0HcV35UatYf9q4ooHSXDOtZzcu6t20/KpW5 +2IQtr6f+JFlNa388/aTURJzXqeMHTTPEQMRicp0V/svHogWYEZiKCfDlW7k3va8V +eBd7m6LI8iBc0V9b2ieRkA16e1sZ2A48iaKTiErTZl3xEc25/S+HOpp/OKEdFr1+ +CxJ5eNKi2i8o3nyRcGz7wa5QgU7esL3V3x+h8ycCtHgAFUqGveqwDSHbBeD5+85y +Dj+STIQ= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/02.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/02.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/02.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/02.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIBAjANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow +HjEcMBoGA1UEAwwTbWFzdGVyMi5leGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBALPe3Hu82akfIiNLnG8vYZVK1+T298lkpWepwGUvdWWL +FO51J/+B9av9dERwA7EQMB2EsqiDRLw90Mb76T5MBqNmgNn4cwjP/Ur6+jCAf7LD +MO9y9tFd+CFcG4gi+mKAUS1o9Yq6wcc3lnAIHZYaYIFsLwE8m0L1IRGbHwrVCwRR +Wm61BqBVVC9s4vGopPJ930IH+D1kNXc5veNwatiWwdK4oXt3ti6NoTbpeAwLWYEo +/62Tw9WJl56TGgLrEamA0vmwZqRQiVPiCcsFGSaSagI6LfsznkjWcdkMWnZBJtky +Ijc6kgTv514oJ9DgorO8cmQ1Nh/8sYWWuu38a/Ec4MkCAwEAAaOB2TCB1jBbBgNV +HSMEVDBSoU2kSzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIg +T3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMB +Af8EAjAAMAsGA1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwPQYDVR0RBDYwNIITbWFzdGVyMi5leGFtcGxlLm9yZ4IHbWFzdGVyMoIGcHVw +cGV0ggxwdXBwZXRtYXN0ZXIwDQYJKoZIhvcNAQELBQADggEBAFIbMGPuJ6FvHeiU +h0BQ9DaPj1kGpqW7SpR9KcfboQDodL7yrkSGdOJ+64zKZr7r388HFpaS/xN+ilay +VRXG1l99O7pOKmvr7t0V9CqWMvuALYV9CLmGIVKzrc2X36nngzaNIHlgiJ6PsbAh +w3V5+z+xUIiHPRU+KHzU0ka5Hn/zTkqkWm0Ci7fTyZAfUMD8gPo0cM79NWlCulej +bAkD3H/KRxBAVo6P0e5xB1bWlS+4LDd8Y6G1l29PAc/FrSgTaW7zz0A9Rf8UMQM+ +Ye/f609U/IIr3W+Q64AU50xlY+ufSuP7KeZanCi0FAS0HuuNdqG4MQWyHXppTgkg +HwYbNEo= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/03.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/03.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/03.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/03.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsTCCApmgAwIBAgIBAzANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow +HTEbMBkGA1UEAwwSYWdlbnQxLmV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA30VndhiWyA0M9LXkutDYYdN9W945RiHn6mjBrCaTyxYh +GunD7Ub2mjoivkgSQt3NlQRRI8bbgKGXYuAJD8tFiZtBYbh394mPHBS3j/cbkc4k +mbnO2v2nR9DUd2TgHJ/gpvSQij7ullKpOc59h36wNAtKtlLpacEEdd6rJBVwIDOy +oUlfO5lW/8Aw9K6nh/SuKLV7a1qIu/s4Lkv+8tQpzkgrnRPh1Nd0OJpoXNuBOfx4 +Nipj07yGV6PWtBWAvOkKXSC7ZxudmlUFc4UHPwLZsh49xjZJGSrspJETexknu9pA +FGqXAqE2kJhAWtehOiy7TKbIuOj1qKE+nEm1Ru44jwIDAQABo4GaMIGXMFsGA1Ud +IwRUMFKhTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0ExGjAYBgNVBAsMEVNlcnZlciBP +cGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExDggECMAwGA1UdEwEB +/wQCMAAwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD +AjANBgkqhkiG9w0BAQsFAAOCAQEAHwlUEuXe8jtnvdX49q3JVYs/wu+NsMg0+jHO +3JKWHz+2xjOYrWK1CgoAADAXE7/KH/7XPho9EqSlR0eANtUvHcOLuruYxrQgPX0T +nkyjLLEF4Bc5rh45XrDtZuvHbhtqhEKMRs9yjlVCCKBUJF4mVH3dNapSnZaXfSOh +VsmK0Ylt2iP11zbD4ncOnCCb4GKIqi0QW4rILdSHy8L3NxWCKP5QV7G0dQAYw0TX +syfA4NvG9SgGA/tR62B/+IO62ZYPTBdVceTWZDs1DolimM5g80DTl9kz3nLaPvNv +k5pnJWP5FV07PVgEf6I5xVok+l2wibewBBTAROa9I0FMfK/k8g== +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/04.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/04.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/04.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/04.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2TCCAsGgAwIBAgIBBDANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow +RTEiMCAGA1UEAwwZbWFzdGVyLWVtYWlsMS5leGFtcGxlLm9yZzEfMB0GCSqGSIb3 +DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAK+DZO+xbJow5kzEP13hC2XkPjY5MZZg7sX5VuGtwhSD0vberSxijokB +RR9U5RAp5AmWEpbJcNuL4wUyQ0yA/E0FSc6BhDwT25aA2l8CiIGpMmLmMw4M80K1 +SXuHeXU/g+7MBmaYC4AxYbsxr1us+c1IpNYl9rO/4ZuUIgOHKjnnBHKYZqpNVmXS +4ss3o74rrHkIWLvBI43Mz4Ct2LZMSkaqqESvIfBKpW9LndtoKyIaaoxnXivC7oiG +pbEfot0pvoCNV7Wfj7zFVQUdKdJTa5XHvshXoFdrnNHfZKVb6AIAbvZidzcBuT54 +Ib4KFjIV3VxfJkQ/U0VTn4zhK+t2EuMCAwEAAaOBmjCBlzBbBgNVHSMEVDBSoU2k +SzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9u +czEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMBAf8EAjAAMAsG +A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZI +hvcNAQELBQADggEBAJ083++L/CwcyFhC3FvaRX6dYQB5LlqPQs7E6GRZq4wL2Boo +rNeWVTOhhjdxjmbLBLJ/VDOzxW5mCIVil8mHbTYUrmt77lfXkeB1Gb4sx8s3SbdK +10mmAhrV7eLjeV1Y/Tt9G4ZU3RbK78O/GXCg5Vp1S6GPUBMxlgm5K1IibpKxz5Cz +yU6EN0seAhKB/MWiSWILcJy8sbySThLDy0wLzvUDFqHlVvj3XtBd08qp1Pov+r73 +pBjgDHJvunxJ93DDfhiMPgoOIMcvijZmOQ+2cFfbhwlyJH3m90PbpaW8H0BNFBLT +csvsTzHsS78o9usu+Sv/NpmDnzvOu1rzSCMmu58= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/05.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/05.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/05.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/certs/05.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID2TCCAsGgAwIBAgIBBTANBgkqhkiG9w0BAQsFADB+MSQwIgYDVQQDDBtJbnRl +cm1lZGlhdGUgQ0EgKG1hc3Rlci1jYSkxHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhh +bXBsZS5vcmcxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMxGjAYBgNVBAsMEVNl +cnZlciBPcGVyYXRpb25zMB4XDTIzMDQxMjIyNDE1OFoXDTQzMDQwNzIyNDE1OFow +RTEiMCAGA1UEAwwZbWFzdGVyLWVtYWlsMi5leGFtcGxlLm9yZzEfMB0GCSqGSIb3 +DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAL5w5qggNQUtGSgga5SPGHr5J8uIrg1IE/WR/p83V2pV+QyAJQChmIhT +sBx87INcl89jo39a7LWoeXa3gUaXgP+6JY4A7E4nEbTveqBr+vKLdeZjyC0DwhIx +4Y6scXD9O2OeXH/qKdEHLnHAtKZBFIHY0YDDVVOrpS7DNtCZe4KTuX/97dxl+Qez +nejIIY8vhYz5Cnhfad5KEKIuoKzj25ZGxcPr3/BeZ8IOr2/vqW6r1qWFCx/7X23Z +VX94c7RYacqiixIUAf1Q6+9EoVhLUctuwnfTCqb5HX2zKrcsVAG9tirg5bQeqF9i +MQDblarSN1LRbItL/0q7ZRSgZ8kMnKUCAwEAAaOBmjCBlzBbBgNVHSMEVDBSoU2k +SzBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9u +czEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQ4IBAjAMBgNVHRMBAf8EAjAAMAsG +A1UdDwQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZI +hvcNAQELBQADggEBABhJx2HxQf/RMxdTzikFAqFL4e6xlOSZtTBO2jgD6gGJiBmR +qHB/2XXhOg6lUUm3woVCC6CCNYv47OHtA2geDA5lBEfLGQJOPMMCR/z98QKX6jVE +SVQNy3DwNmuWBTCBA3L9ArQ9FL2NhXnDhyOjVM/u4/dpYNXegzeA4du8BVblObo+ +UY28OtNkZ72MSSGV95kAiqltBSYEJhFw8c57kAeIrgOgWy2XKaUToLICSV19BYAM +IzR2HLwtmAYvga59xqSKyqmnJXiZpJqEae9XwR4JsWuYN5nwualobsAR8JPI46vU +05SYeaz1hwYollWM0iIQcXwArWFyvLXGFApQdQc= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,5 @@ +R 430407224157Z 230412224158Z 01 unknown /CN=master1.example.org +V 430407224158Z 02 unknown /CN=master2.example.org +V 430407224158Z 03 unknown /CN=agent1.example.org +V 430407224158Z 04 unknown /CN=master-email1.example.org/emailAddress=test@example.com +V 430407224158Z 05 unknown /CN=master-email2.example.org/emailAddress=test@example.com diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.attr.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/inventory.txt.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,5 @@ +V 430407224157Z 01 unknown /CN=master1.example.org +V 430407224158Z 02 unknown /CN=master2.example.org +V 430407224158Z 03 unknown /CN=agent1.example.org +V 430407224158Z 04 unknown /CN=master-email1.example.org/emailAddress=test@example.com +V 430407224158Z 05 unknown /CN=master-email2.example.org/emailAddress=test@example.com diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/openssl.conf puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/openssl.conf --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/openssl.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/openssl.conf 2024-01-15 23:29:55.000000000 +0000 @@ -5,15 +5,15 @@ # Root CA [root_ca_config] -certificate = /tmp/certchain.KDOYxTc2/master-ca/ca-master-ca.crt -private_key = /tmp/certchain.KDOYxTc2/master-ca/ca-master-ca.key -database = /tmp/certchain.KDOYxTc2/master-ca/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/master-ca/certs -serial = /tmp/certchain.KDOYxTc2/master-ca/serial +certificate = /tmp/certchain.HHLFb2ep/master-ca/ca-master-ca.crt +private_key = /tmp/certchain.HHLFb2ep/master-ca/ca-master-ca.key +database = /tmp/certchain.HHLFb2ep/master-ca/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/master-ca/certs +serial = /tmp/certchain.HHLFb2ep/master-ca/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 policy = root_ca_policy x509_extensions = root_ca_exts @@ -31,30 +31,30 @@ # Master CA [master_ca_config] -certificate = /tmp/certchain.KDOYxTc2/master-ca/ca-master-ca.crt -private_key = /tmp/certchain.KDOYxTc2/master-ca/ca-master-ca.key -database = /tmp/certchain.KDOYxTc2/master-ca/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/master-ca/certs -serial = /tmp/certchain.KDOYxTc2/master-ca/serial +certificate = /tmp/certchain.HHLFb2ep/master-ca/ca-master-ca.crt +private_key = /tmp/certchain.HHLFb2ep/master-ca/ca-master-ca.key +database = /tmp/certchain.HHLFb2ep/master-ca/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/master-ca/certs +serial = /tmp/certchain.HHLFb2ep/master-ca/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 policy = master_ca_policy x509_extensions = master_ca_exts # Master CA (Email) [master_ca_email_config] -certificate = /tmp/certchain.KDOYxTc2/master-ca/ca-master-ca.crt -private_key = /tmp/certchain.KDOYxTc2/master-ca/ca-master-ca.key -database = /tmp/certchain.KDOYxTc2/master-ca/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/master-ca/certs -serial = /tmp/certchain.KDOYxTc2/master-ca/serial +certificate = /tmp/certchain.HHLFb2ep/master-ca/ca-master-ca.crt +private_key = /tmp/certchain.HHLFb2ep/master-ca/ca-master-ca.key +database = /tmp/certchain.HHLFb2ep/master-ca/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/master-ca/certs +serial = /tmp/certchain.HHLFb2ep/master-ca/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 email_in_dn = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/serial.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/serial.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/master-ca/serial.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/master-ca/serial.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +05 diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crl puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crl --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crl 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crl 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBjjB4MA0GCSqGSIb3DQEBCwUAMEkxEDAOBgNVBAMMB1Jvb3QgQ0ExGjAYBgNV +BAsMEVNlcnZlciBPcGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExD +Fw0yMzA0MTIyMjQxNTdaFw00MzA0MDcyMjQxNTdaMA0GCSqGSIb3DQEBCwUAA4IB +AQAtt9DKR6C9F5ARt9YxGa+p9sSsNDKm8GrD1Tj2+wuYzybH6hMH8sPD1+MQ3LfH ++L01Bu92Lnc9t3V8UCnjQuW8UbqzbxaYOUxGmdr8waQ9Sk8FvzxjZq2635OyZHDM +LcsGPCZvXG3pNN9V/hnjxSK9EpXz8Tw3t0Wb9+fR5aRWnW8Cx/8vkyj9IZQEgPJ9 +YWngFA0LJuAHNNXcCiu1KDVFbeiaoAY6nbI8u/ZGtfab+EMfNVkbKWr0/j/kVis/ +LdvRRLFg2Dej6UfaktgfJTIircbPMt0+0M/EqW+ifkljlqaUv1wMawJOw+gJdNxc +12GvgcZUh5Gj1HaYV2SY7OD/ +-----END X509 CRL----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.crt 2024-01-15 23:29:55.000000000 +0000 @@ -1,21 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDZTCCAk2gAwIBAgIJALG5BHL7ESfjMA0GCSqGSIb3DQEBBQUAMEkxEDAOBgNV -BAMMB1Jvb3QgQ0ExGjAYBgNVBAsMEVNlcnZlciBPcGVyYXRpb25zMRkwFwYDVQQK -DBBFeGFtcGxlIE9yZywgTExDMB4XDTE0MDQwODAxMjUzNloXDTM0MDQwMzAxMjUz -NlowSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlv -bnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDFDXbR+00AwXM+HuMIpw8eVWBzQWBqDCYkX3IvYRGj+w9y -7AitrN+J0MZE3pbaRvlH5wU7MShFOmT0k/B/wrylW4W5G/iAtd2ZnXicBPrA9zDU -eHJftQxR7+Qjmsc1BqVf43PUlQITpn1APgXDzPJdk9XbRWEsIycuXkwTXzVND0U5 -z3dGS/oh9yMim0DnF2oQ+gTFA9n17xOD5hBN80U3fn4DXtcFGbtXOj6zBHsxgLCi -leif2AB1oAaZ0lqkwk6Se0rFd3zafYLDAwCPCWlZSfkQ0C/W7WYx07PDRxSYs1H6 -Viz2uHwqzyD6elxvJBGcrLdvDqTLL+w0ag3yMPWbAgMBAAGjUDBOMB0GA1UdDgQW -BBQWr4Al4/rqSL+RM2YB/VHvJGdMYzAfBgNVHSMEGDAWgBQWr4Al4/rqSL+RM2YB -/VHvJGdMYzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQC/sFnu1TIr -L6HhTft5aUaeLuO/329cDUHxlUppGRYrctkZvYK4b8TBi2BD+tcwRKS1kh4nrQhr -xaBO+oUmyJeNwEPk40trzusV9N9tfqw8drBBXEVZGxrYRYovq/RqLfUQ224EF3z0 -r74dAWL0R80PvVzeJfUsUw0KYgskfLzP5QSW1rrJnutfYP95EMV4yWyrNqnDko3M -v7XENh0TMEolMxPZ+X3TqT6Q0j4aM8njswObyeABslt+nC6nLfgBvgDaSvEULPL6 -u5aWNxp9WudGqGBvHoR6OXdZDRCzWSz52jnvXiZE4E0VnqsWxCmjDGECke4TRoMU -rtMLavKgCsIe +MIIDczCCAlugAwIBAgIULxTl39bugVS0Jwy4ZWpV3IIWTjcwDQYJKoZIhvcNAQEL +BQAwSTEQMA4GA1UEAwwHUm9vdCBDQTEaMBgGA1UECwwRU2VydmVyIE9wZXJhdGlv +bnMxGTAXBgNVBAoMEEV4YW1wbGUgT3JnLCBMTEMwHhcNMjMwNDEyMjI0MTU3WhcN +NDMwNDA3MjI0MTU3WjBJMRAwDgYDVQQDDAdSb290IENBMRowGAYDVQQLDBFTZXJ2 +ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7EDBWTX9RTzT1VrDqPM1TKtQejvJ+8 +RXmOleEWc9dF6nREnwPOozrCx9E2AgopEFLGpo/2oYFOPMoXAMCTmga1YYq+mls5 +9lYFB2eaL9hbTe6GaDXV13c2eQexfR+3I/b3aRmQv/w6jnHlQCqmvxZ3ttVoc6f2 ++aVjBvOTG9Bt2/N9BrslW1//9QaNUIVR7QRa9mOEICZ/ZCwdDDt5L0co5jsqRMgO +tiWsu4Q2XtLWiBzhp61+cVLSdxabkWy127qN/dBRMbGk2aCCjqIvQaAZy2rv4LL/ +JDhnLC5s/e5IdQ0ArJBhaOhDz5n38u3TSWpiWMywmMEbs+IDHySvqakCAwEAAaNT +MFEwHQYDVR0OBBYEFPWQhkterSX1ED/ePsbUn0I/h6qvMB8GA1UdIwQYMBaAFPWQ +hkterSX1ED/ePsbUn0I/h6qvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAGMECPlztKldxlL6+s5XdHx7hEm27DnJ/YhFZek6kE23dIkaAszHvmNU +Z7HY64XcwS95XHC/0mwnv5t/aIrxvAXHsVrvOB4t6BrvVxXeUaF51LxoE69gDHsZ +Y1BhwULu6+i2i779O5AKniAh7gvD3MzEp62cD4ZIjauB/UXu9l9ww75kqKBiNuUL +vA7Wttc5u/3b3i2nqJOXZUcl5AVzLyDfBofh+yENiZ7Ra0gy0lLiZ6PF5eevMrhC +46HWVIPk7YAeqGMCeu/wSh177AOI7C1uQ7QLXOM09NUHB0AACmNIxY0TOGJCM8Vn +10RDUEG3ImtPZhKxXZIVUOcT162RYbI= -----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.key puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.key --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.key 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/ca-root.key 2024-01-15 23:29:55.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFDXbR+00AwXM+ -HuMIpw8eVWBzQWBqDCYkX3IvYRGj+w9y7AitrN+J0MZE3pbaRvlH5wU7MShFOmT0 -k/B/wrylW4W5G/iAtd2ZnXicBPrA9zDUeHJftQxR7+Qjmsc1BqVf43PUlQITpn1A -PgXDzPJdk9XbRWEsIycuXkwTXzVND0U5z3dGS/oh9yMim0DnF2oQ+gTFA9n17xOD -5hBN80U3fn4DXtcFGbtXOj6zBHsxgLCileif2AB1oAaZ0lqkwk6Se0rFd3zafYLD -AwCPCWlZSfkQ0C/W7WYx07PDRxSYs1H6Viz2uHwqzyD6elxvJBGcrLdvDqTLL+w0 -ag3yMPWbAgMBAAECggEAPLxxqzcx/NGjm7oZcElNt6PJHr0/4m/sMbnZkBDzRv/T -OzDVW/K0092tvvx8srq9ixQB4MS+DNh61Yfj0P1M+ArFpNCiP8nOi9KlojFGuMN4 -IYUee7FqIrc2MmM6k3WA85U/1n43LadbY7m6PSembIFsoTE5Sklrgjc+a5ok66MG -1m2bZuQnfQs146T5/tZ7cFpvLKL0EUTSFLblXUeugnesQI/z17XcveEm40T0RleU -oKHYeldNYTzcRCRu5riZoOCeAiBeKvmc2Yk8Dx2ump8u2Ir5G6ST0oVP0tZ9On++ -wBfmhkJzN7wmRURk/Ivu1/Kq9/PGsJI4v4TBQbUZAQKBgQDhIw/55KmFbwKUBku3 -Gegf6R7NQjn/HccBlN/zUCfV9IJs2V4J96/7B7S3JYfwoCQp9uhZJ1Qe/aWIY9IY -UmwZLb9QhXWQbuMJtfqgrWy4FoRqAe469Mr+ol44v4j45ZfiMF+BY1sQjZuIdUNv -YrUGkFzj+ETeUUPDPcZHXLDCawKBgQDgEM+wOGBFcBuONm6XpJ/Mp4dGbSqRfRm7 -bo4lqF0UFpm9HKsS6WY/ZsQGOrTq8bsU6aTttPGvenwYm/ySEpI5iBbvXe/fgH3i -Wehd81UkiHO4uzdNgyo30aZRpa1644G4LkBlPKrcU+hKiVZtshZJjd05BQyBLKeb -NByETARFkQKBgC/O/0yEt08DlNuUPq3iTX3BRm42GxTG4QS/9ZK4uczggHXW7vZU -58T6DPE7ghOiHivfJ9YO2Pk/ydAdynapwhTStybFQILsWUAtqcxHJ6gr9/B++nUA -mL7cAgAxBEg+kTNSLGXQkH0CZT3kEO7tWh3LuD4c8Zr4TNiAHMP4tfyDAoGBAKbb -Xg54rRiIl7ybxFeg/G9HAnHrsZuOca8mcSyR6F6hRfOSecMnlED6oleROFENmqfE -JlHuQVzP9cHNx5Rvx/yX35x5c7wYz6XUFjqAjpMaGjMjF9fdKX9P3G9I1ZuB+IPT -zZiWSRayVXXMOFSQHhDAWFGx9NHtExN9Bw9uHHBBAoGBAMfC/cikwe+JjiNRMeUy -5A+KMfKYZ/63UctCZXgx+MbcOmt4LW/JYBGJG9l8ekGZVxprAMTTePsscYHQxPct -O+JvTnDJc7q3Jqmk3+eZMoNkIY0JWMR9qKTy4gmz3NMQetTpP3W3QWswALP6pKOV -5whba0lHsqlXpZzsxV/rcpuk +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOxAwVk1/UU809 +Vaw6jzNUyrUHo7yfvEV5jpXhFnPXRep0RJ8DzqM6wsfRNgIKKRBSxqaP9qGBTjzK +FwDAk5oGtWGKvppbOfZWBQdnmi/YW03uhmg11dd3NnkHsX0ftyP292kZkL/8Oo5x +5UAqpr8Wd7bVaHOn9vmlYwbzkxvQbdvzfQa7JVtf//UGjVCFUe0EWvZjhCAmf2Qs +HQw7eS9HKOY7KkTIDrYlrLuENl7S1ogc4aetfnFS0ncWm5Fstdu6jf3QUTGxpNmg +go6iL0GgGctq7+Cy/yQ4ZywubP3uSHUNAKyQYWjoQ8+Z9/Lt00lqYljMsJjBG7Pi +Ax8kr6mpAgMBAAECggEBALGzA7+3PPC6ZPWFn9NkJvWEkTjTm9ScdgkWZfFg3oXw +7KKoJxGakeZ6aEiBZhddW04ItDWd/QeoOc5lHpxjPkjtsbsQPJCSrAcZo1eCyAPl +1IBeu0kpU1h9VF8sRyDuwNYLpCP4p0ca/DkekZsWW7vvfw7CCkQcmkltZhgIIitp +8qW7izPYoggPBJqaJP/8z3j6J3oCJ/r6GGzV2h/yKXLVcgw5SZ8sLQxwoakbFqAy +dxRSwQ3Rb3IUX0GTbFHePZ4CJ57PpZgyFfPfUwRmIKnS/UJpymywBBVvIpEKOduC +xqRUSu/kR8GEnsJsZtOzkFdaXIN7OBzWbJYPtOW1VVECgYEA+NQ+O38EhOU0GjGY +JCniB7F4RQVKVi95PWODOsgnx18ItFKGK5eHsrStpsOgni0Ks4JMQvUdpgGfrYYa +5jhIPQTcylbrvwiEydUh4pIzsKm5wrRzEr5lHHyAL4dFMO0S62QUgqFucDfGJqOB +hJpzx94delXIrPfIcdu6J4At+b8CgYEA1Ll6moOKyQ8uVfFqedCQ5VodLXMNnpAY +OcTYZnfTUU3HMoBJraGoGdrtPIX8WLHTpsIQNv5coAgTv0PzTnpuBbOHnWO6efOm +D4NJOWrTWJpddOFvKlHIsjTJxVQtCrOX9RMQtcJNIVngiuoogOfcWf31LzIhVkT+ +7h6EsfNBJpcCgYBMsOXJYpma+CW52QVCSCJhKqKd60aBD7q3X/9nU59nBHRjV3gA +4YAyZRuMZYkQl2NP9l3Y2UFdE+0KB2YtBLXHEMOYHXglddOvfWAnO9Zwoa8eQo2g +/kudnwXHIFMEBF2rbTohFO476gTJjeVyYERFGsFNHjlujiQaa5WRbSE+cwKBgQCt +ZB4eUfhPGFOxc91RO4LPn5tGz5AbXeKRO/UT2LnjfJ1QeacUoz7tDDjxa1kcRp55 +P5eN69k+DDdkYJOytTJ27TYuQPrfqo0B+gY2ie+JOhHRUmuVOCJ5t3N7p7grD+Zd +udOWA7Fw7NyM/n5TTCqdI9a1SIxJ0GVnXHLWxaumMwKBgBBtS0ZpDBv3YtTpOUFe +BekF2VrWh+1OXWZhmAEbBYHiaYJ3JVapcZbiU4uoVzo5mDRiXL8MxfJE+VmKrewl +IKIjAuA6PhsNKJWCBptoIOOdmQnl0F/NRiy7+7sc+oNeHggp0zrRGoowtAUWgOzr +6EKRSKjF3GiMdb5RfS6DDNBO -----END PRIVATE KEY----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/certs/01.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/certs/01.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/certs/01.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/certs/01.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6jCCAtKgAwIBAgIBATANBgkqhkiG9w0BAQsFADBJMRAwDgYDVQQDDAdSb290 +IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBs +ZSBPcmcsIExMQzAeFw0yMzA0MTIyMjQxNTdaFw00MzA0MDcyMjQxNTdaMH0xIzAh +BgNVBAMMGkludGVybWVkaWF0ZSBDQSAoYWdlbnQtY2EpMR8wHQYJKoZIhvcNAQkB +FhB0ZXN0QGV4YW1wbGUub3JnMRkwFwYDVQQKDBBFeGFtcGxlIE9yZywgTExDMRow +GAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJ1EAzT0LLWHkeo8meKuMjMR2RI53lHwkSWNohBOPVA3lY+5UGei +lyrkIb9H22ZR8DnNz7JjkJkC6Uu55PwFh2d2Q60wZLqlX21jRvmsCzfdGt/brD6Q +q43mAJp7MyD3+31lmv2wmKvMPWoq6u+tjjDbtEey7HAiJxQ/KCGPqcUv5RFmqKz2 +pfteWFxqHQUmn4JABZpte7Fl4/dQyw/6r7ctEnKqVuF2lQzCS0awy3oYQr6Qwwbs +HNTApjmW2EXTuWYTYQ/ftQeGN5QdQm+9fiKi45Z/HsY1z7vyUY5TjjSLITV0a420 +v2WhW8ckMWrllwaCEzmfNcjx9W5FQm4w8+MCAwEAAaOBqDCBpTCBhAYDVR0jBH0w +e4AU9ZCGS16tJfUQP94+xtSfQj+Hqq+hTaRLMEkxEDAOBgNVBAMMB1Jvb3QgQ0Ex +GjAYBgNVBAsMEVNlcnZlciBPcGVyYXRpb25zMRkwFwYDVQQKDBBFeGFtcGxlIE9y +ZywgTExDghQvFOXf1u6BVLQnDLhlalXcghZONzAPBgNVHRMBAf8EBTADAQH/MAsG +A1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEALw/RGiXdEZQ+rj43BqeOlAas +mUpHnhmc9XPVNtkgRTf9DYc/NxRFBLNC8eBnThcZGTlN7c025xYkjQsIvs6gKD+y +SKFLv0+eHuCywrDgAZCAcFC+ekXu5b7BfKMfjkWJHELYEIomgVHY4mj6blZ5lmWk +Mmr8qYzDAWYD6Rits21YMKyMWcAcoTed9GQFwZOhLJHAG4lvoRd6qXAK+U7dIoQh +vf5CGFP2YO71un22IGRy5QJhoDyxV3yHbbiv7zS+NPn1bOQOfKRGu58PxfsyO3BT +aWGyMzLhxPB6ufAQo7fWX1jOqPxJ1aMS9+aAba7QW/QrOX+LAlAulS034izUFA== +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/certs/02.pem puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/certs/02.pem --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/certs/02.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/certs/02.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6zCCAtOgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBJMRAwDgYDVQQDDAdSb290 +IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBs +ZSBPcmcsIExMQzAeFw0yMzA0MTIyMjQxNTdaFw00MzA0MDcyMjQxNTdaMH4xJDAi +BgNVBAMMG0ludGVybWVkaWF0ZSBDQSAobWFzdGVyLWNhKTEfMB0GCSqGSIb3DQEJ +ARYQdGVzdEBleGFtcGxlLm9yZzEZMBcGA1UECgwQRXhhbXBsZSBPcmcsIExMQzEa +MBgGA1UECwwRU2VydmVyIE9wZXJhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDAEPlZmffi8ZVgLS29BkwS1xSvWa/LAzL0oSN99sRcEXf5Aq94 +n9uEq7XkooaM+/gfUBI1KGJBLwbKKtXc0xL0i9JGjMcFPMnlU5wCcgzK5KArB5OZ +RV5MM06j86+/wDYDNojYxb1lvePJzv3qdKwkEVtIWRfA29FaMoEsqWWZvEsW5fb2 +hE16JEsTJmHDdPu/AlvwXTmy4lkitS8quTMLkpedtz/Sp5wEuQFmgEmeHkZzRuD0 +rpQe+xbDTSsnlib30Mjqi/dUgolseJqCX0RZ8rdxTARUXn+3aYwnZtS18Y12aj5h +igBzVBZvxETE++MluDnZKATH2Tupejzx0Ts9AgMBAAGjgagwgaUwgYQGA1UdIwR9 +MHuAFPWQhkterSX1ED/ePsbUn0I/h6qvoU2kSzBJMRAwDgYDVQQDDAdSb290IENB +MRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBsZSBP +cmcsIExMQ4IULxTl39bugVS0Jwy4ZWpV3IIWTjcwDwYDVR0TAQH/BAUwAwEB/zAL +BgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAKqGMvKEylycCdwVe5idgQhF +u0L81gLr4lcAo6AxCDQXbijD2G1n3SZFl6L/kbUf+S/n3Gk23uE6/Ml5EZYh4Z1V +faNQeH2FHJmsLlPluNAlyCnL5x4KzExM4kRa/UKAf1nOg1ulkRzAAD5WofWipU9k +W+x29VO1eibL0rsJehiASj5SqW9Vm5ZFz4vHeFxBPDNkyAqjPGXc8yZELjrWb0id +nIP2nMN7PNZlWivwBzUwszJU0AxpNYDGbFBlI3ImpUxC5JvpF48ST0V7JzX4HWl7 +IqJqxuDCIkFyiX6eU58n1MxXMsfzCZwR/F3iFOxHDmZjpRDqZAa77WkNYI6QVXA= +-----END CERTIFICATE----- diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt 2024-01-15 23:29:55.000000000 +0000 @@ -1,2 +1,2 @@ -V 340403012536Z 01 unknown /CN=Intermediate CA (agent-ca)/emailAddress=test@example.org/O=Example Org, LLC/OU=Server Operations -V 340403012537Z 02 unknown /CN=Intermediate CA (master-ca)/emailAddress=test@example.org/O=Example Org, LLC/OU=Server Operations +V 430407224157Z 01 unknown /CN=Intermediate CA (agent-ca)/emailAddress=test@example.org/O=Example Org, LLC/OU=Server Operations +V 430407224157Z 02 unknown /CN=Intermediate CA (master-ca)/emailAddress=test@example.org/O=Example Org, LLC/OU=Server Operations diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.attr.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/inventory.txt.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +V 430407224157Z 01 unknown /CN=Intermediate CA (agent-ca)/emailAddress=test@example.org/O=Example Org, LLC/OU=Server Operations diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/openssl.conf puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/openssl.conf --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/openssl.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/openssl.conf 2024-01-15 23:29:55.000000000 +0000 @@ -5,15 +5,15 @@ # Root CA [root_ca_config] -certificate = /tmp/certchain.KDOYxTc2/root/ca-root.crt -private_key = /tmp/certchain.KDOYxTc2/root/ca-root.key -database = /tmp/certchain.KDOYxTc2/root/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/root/certs -serial = /tmp/certchain.KDOYxTc2/root/serial +certificate = /tmp/certchain.HHLFb2ep/root/ca-root.crt +private_key = /tmp/certchain.HHLFb2ep/root/ca-root.key +database = /tmp/certchain.HHLFb2ep/root/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/root/certs +serial = /tmp/certchain.HHLFb2ep/root/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 policy = root_ca_policy x509_extensions = root_ca_exts @@ -31,30 +31,30 @@ # Master CA [master_ca_config] -certificate = /tmp/certchain.KDOYxTc2/root/ca-root.crt -private_key = /tmp/certchain.KDOYxTc2/root/ca-root.key -database = /tmp/certchain.KDOYxTc2/root/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/root/certs -serial = /tmp/certchain.KDOYxTc2/root/serial +certificate = /tmp/certchain.HHLFb2ep/root/ca-root.crt +private_key = /tmp/certchain.HHLFb2ep/root/ca-root.key +database = /tmp/certchain.HHLFb2ep/root/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/root/certs +serial = /tmp/certchain.HHLFb2ep/root/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 policy = master_ca_policy x509_extensions = master_ca_exts # Master CA (Email) [master_ca_email_config] -certificate = /tmp/certchain.KDOYxTc2/root/ca-root.crt -private_key = /tmp/certchain.KDOYxTc2/root/ca-root.key -database = /tmp/certchain.KDOYxTc2/root/inventory.txt -new_certs_dir = /tmp/certchain.KDOYxTc2/root/certs -serial = /tmp/certchain.KDOYxTc2/root/serial +certificate = /tmp/certchain.HHLFb2ep/root/ca-root.crt +private_key = /tmp/certchain.HHLFb2ep/root/ca-root.key +database = /tmp/certchain.HHLFb2ep/root/inventory.txt +new_certs_dir = /tmp/certchain.HHLFb2ep/root/certs +serial = /tmp/certchain.HHLFb2ep/root/serial default_crl_days = 7300 default_days = 7300 -default_md = sha1 +default_md = sha256 email_in_dn = yes diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/serial.old puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/serial.old --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/fixtures/root/serial.old 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/fixtures/root/serial.old 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1 @@ +02 diff -Nru puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/split_external_cas.rb puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/split_external_cas.rb --- puppetserver-7.9.5/acceptance/suites/tests/certificate_authority/split_external_cas.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/certificate_authority/split_external_cas.rb 2024-01-15 23:29:55.000000000 +0000 @@ -1,14 +1,7 @@ -begin - require 'puppet_x/acceptance/external_cert_fixtures' -rescue LoadError - $LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__)) - require 'puppet_x/acceptance/external_cert_fixtures' -end +require 'puppet_x/acceptance/external_cert_fixtures' confine :except, :type => 'pe' -skip_test "Test only supported on Jetty" unless @options[:is_puppetserver] - # Verify that a trivial manifest can be run to completion. # Supported Setup: Single, Root CA # - Agent and Master SSL cert issued by the Root CA @@ -160,19 +153,4 @@ end end -create_remote_file master, "#{jetty_confdir}/webserver.conf", - fixtures.jetty_webserver_conf_for_rogue_master - -# The error messaging around this has changed with the merge of -# PUP-9094. This should be uncommented and updated once that is fixed. -#with_puppet_running_on(master, master_opts) do -# step "Agent refuses to connect to a rogue master" -# on master, puppet_agent("#{agent_cmd_prefix} --ssl_client_ca_auth=#{testdir}/ca_master.crt --test"), :acceptable_exit_codes => (0..255) do -# assert_no_match /Creating a new SSL key/, stdout -# assert_match /certificate verify failed/i, stderr -# assert_match /The server presented a SSL certificate chain which does not include a CA listed in the ssl_client_ca_auth file/i, stderr -# assert exit_code == 1 -# end -#end - step "Finished testing External Certificates" diff -Nru puppetserver-7.9.5/acceptance/suites/tests/code_commands/code_scripts.rb puppetserver-8.4.0/acceptance/suites/tests/code_commands/code_scripts.rb --- puppetserver-7.9.5/acceptance/suites/tests/code_commands/code_scripts.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/code_commands/code_scripts.rb 2024-01-15 23:29:55.000000000 +0000 @@ -171,7 +171,7 @@ forge 'forge.puppetlabs.com' # Forge Modules -mod 'puppetlabs/ntp', '9.1.0' +mod 'puppetlabs/ntp' mod 'puppetlabs/stdlib' EOF create_remote_file(master, "#{git_local_repo}/Puppetfile", puppetfile) diff -Nru puppetserver-7.9.5/acceptance/suites/tests/https/client_may_use_external_cert_chains.rb puppetserver-8.4.0/acceptance/suites/tests/https/client_may_use_external_cert_chains.rb --- puppetserver-7.9.5/acceptance/suites/tests/https/client_may_use_external_cert_chains.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/acceptance/suites/tests/https/client_may_use_external_cert_chains.rb 2024-01-15 23:29:55.000000000 +0000 @@ -12,7 +12,8 @@ generate_self_signed_cert = "openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout #{server_key} -out #{server_cert} -batch -addext 'subjectAltName = DNS:#{master}'" run_server = "/opt/puppetlabs/puppet/bin/ruby #{server_rb} &" kill_server = 'ps -ef | grep server.rb | grep -v grep | ruby -ne \'puts $_.split[1]\' | xargs -r kill' # `xargs -r` is a GNUism. - wait_for_server = "while [[ 0 -ne `curl -ksw '%{exitcode}' 'https://#{master}:7777/' | tail -1` ]]; do echo 'sleeping and waiting'; sleep 2; done" + wait_for_server = "for i in {1..40}; do if [[ 0 -ne `curl -ksw '%{exitcode}' 'https://#{master}:7777/' | tail -1` ]]; then echo 'sleeping and waiting'; sleep 2; else break; fi; done" + check_for_webrick = %q{/opt/puppetlabs/puppet/bin/ruby -e "require 'webrick'"} server_script = < Fri, 01 Mar 2024 18:08:21 -0500 + +puppetserver (8.4.0-1~exp1) experimental; urgency=medium + + [ Jérôme Charaoui ] + * New upstream version 8.4.0 + * un-bundle concurrent-ruby gem + * d/control: + - drop hiera 3 dependency + - drop direct facter dependency + - remove puppet-module-*-core Recommends + + bump puppet-agent dependency to 8 + + update clojure library dependencies + + add binary depends on procps (Closes: #1050389) + * d/patches: + - drop patch merged upstream + + refresh patches for new upstream version + + add patch to switch back to jetty9 + + resolve ftbfs with java 21 + * d/rules: + - drop clj-yaml from classpath + - drop hiera 3 gem from test environment + * d/tests: + + fix gem list test + + add test for access log + + add regression test for #1063568 + * workaround for non-C locales (Closes: #1063568) + * remove unrecongnized lintian tag + + [ Thomas Goirand ] + * Add myself as uploader. + * Add puppet-module-puppetlabs-mailalias-core as Recommends + (Closes: #1050337). + + -- Jérôme Charaoui Sun, 18 Feb 2024 09:56:24 -0500 + puppetserver (7.9.5-2) unstable; urgency=medium * abort service start/reload if mainpid dies (Closes: #1032241) diff -Nru puppetserver-7.9.5/debian/control puppetserver-8.4.0/debian/control --- puppetserver-7.9.5/debian/control 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/control 2024-03-01 23:08:21.000000000 +0000 @@ -5,11 +5,10 @@ Uploaders: Louis-Philippe Véronneau , Jérôme Charaoui , + Thomas Goirand , Build-Depends: debhelper-compat (= 13), default-jdk-headless, - facter (>= 4.0.44), - hiera (>= 3.3.2), javahelper, leiningen, libbeckon-clojure, @@ -20,7 +19,7 @@ libcommons-io-java (>= 2.8.0), libcommons-lang-java, libdujour-version-check-clojure, - libjruby-utils-clojure (>= 4.0.3-3), + libjruby-utils-clojure (>= 5.0.1), libjson-simple-java (<= 3.0), libkitchensink-clojure (>= 3.2.1), liblambdaisland-uri-clojure, @@ -30,6 +29,7 @@ libpuppetlabs-i18n-clojure (>= 0.9.2-2), libpuppetlabs-ring-middleware-clojure (>= 1.3.1-3), libraynes-fs-clojure (>= 1.5.2), + librbac-client-clojure (>= 1.1.5-2), libring-basic-authentication-clojure, libring-mock-clojure , libsemver-clojure, @@ -48,8 +48,10 @@ libyaml-snake-java (>= 1.33), maven-repo-helper, python3-docutils, - puppet-agent (>= 7.22.0-2), + puppet-agent (>= 8~~), + puppet-agent (<< 9~~), rename, + ruby-concurrent (>= 1.2.2), ruby-deep-merge, ruby-fast-gettext, ruby-gettext, @@ -69,12 +71,10 @@ Architecture: all Depends: default-jre-headless, - facter (>= 4.0.44), - hiera (>= 3.3.2), jruby, libcomidi-clojure (>= 0.3.2), libcommons-io-java (>= 2.8.0), - libjruby-utils-clojure (>= 4.0.3), + libjruby-utils-clojure (>= 5.0.1), libkitchensink-clojure (>= 3.2.0), libprismatic-schema-clojure (>= 1.1.12), libpuppetlabs-http-client-clojure (>= 2.1.0), @@ -85,21 +85,19 @@ libtrapperkeeper-metrics-clojure (>= 1.5.0), libtrapperkeeper-webserver-jetty9-clojure (>= 4.1.0-2), libraynes-fs-clojure (>= 1.5.2), + librbac-client-clojure (>= 1.1.5), libssl-utils-clojure (>= 3.5.0), libyaml-snake-java (>= 1.33), - puppet-agent (>= 7.21.0-2), + procps, + puppet-agent (>= 8~~), + puppet-agent (<< 9~~), ruby, ruby-puppet-resource-api (>= 1.8.14), ${rubyDepends}, ${java:Depends}, ${misc:Depends}, Recommends: - puppet-module-puppetlabs-augeas-core, - puppet-module-puppetlabs-cron-core, - puppet-module-puppetlabs-host-core, - puppet-module-puppetlabs-mount-core, - puppet-module-puppetlabs-selinux-core, - puppet-module-puppetlabs-sshkeys-core, + puppet-module-puppetlabs-mailalias-core, Description: configuration management system, server Puppet is a configuration management system that allows you to define the state of your IT infrastructure, then automatically enforces the correct diff -Nru puppetserver-7.9.5/debian/copyright puppetserver-8.4.0/debian/copyright --- puppetserver-7.9.5/debian/copyright 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/copyright 2024-03-01 23:08:21.000000000 +0000 @@ -6,10 +6,6 @@ Copyright: 2013-2020, Puppet Labs Inc. License: Apache-2.0 -Files: rubygem-concurrent-ruby/* -Copyright: Copyright (c) Jerry D'Antonio and other contributors -License: Expat - Files: debian/* Copyright: 2020-2022, Louis-Philippe Véronneau 2023, Jérôme Charaoui @@ -38,32 +34,3 @@ . On Debian systems, the complete text of the Apache License 2.0 can be found in "/usr/share/common-licenses/Apache-2.0" - -License: Expat - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to permit - persons to whom the Software is furnished to do so, subject to the - following conditions: - . - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN - NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - USE OR OTHER DEALINGS IN THE SOFTWARE. - -License: GPL-3+ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 3, or (at your option) any - later version. - . - On Debian systems, the complete text of version 3 of the GNU General - Public License can be found in '/usr/share/common-licenses/GPL-3'. diff -Nru puppetserver-7.9.5/debian/manpages/puppetserver-ca.rst puppetserver-8.4.0/debian/manpages/puppetserver-ca.rst --- puppetserver-7.9.5/debian/manpages/puppetserver-ca.rst 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/manpages/puppetserver-ca.rst 2024-03-01 23:08:21.000000000 +0000 @@ -7,7 +7,7 @@ ---------------------------------- :Author: Louis-Philippe Véronneau -:Date: 2023 +:Date: 2024 :Manual section: 1 Synopsis @@ -63,6 +63,9 @@ The following subcommands require Puppet Server to be stopped: +``delete`` `` ... + Delete signed certificate(s) from disk + ``import`` `` ... Import an external CA chain and generate server PKI @@ -88,6 +91,11 @@ | --certname `NAME[,NAME]` One or more comma separated certnames | --config `CONF` Custom path to puppet.conf +| **delete**: +| --config `CONF` Path to puppet.conf +| --expired Delete expired signed certificates +| --revoked Delete signed certificates that have already been revoked + | **enable**: | --config `CONF` Path to puppet.conf | --infracrl Create auxiliary files for the infrastructure-only CRL @@ -119,6 +127,11 @@ | **prune**: | --config `CONF` Path to the puppet.conf file on disk +| --remove-duplicates Remove duplicate entries from CRL(default) +| --remove-expired Remove expired entries from CRL +| --remove-entries Remove entries from CRL +| --serial `NUMBER[,NUMBER]` Serial numbers(s) in HEX to be removed from CRL +| --certname `NAME[,NAME]` Name(s) of the cert(s) to be removed from CRL | **revoke**: | --certname `NAME[,NAME]` One or more comma separated certnames diff -Nru puppetserver-7.9.5/debian/patches/0001-Lein_Local.patch puppetserver-8.4.0/debian/patches/0001-Lein_Local.patch --- puppetserver-7.9.5/debian/patches/0001-Lein_Local.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0001-Lein_Local.patch 2024-03-01 23:08:21.000000000 +0000 @@ -9,30 +9,23 @@ project.clj | 160 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 60 deletions(-) -diff --git a/project.clj b/project.clj -index d3a54da..dd3b14b 100644 ---- a/project.clj -+++ b/project.clj -@@ -27,65 +27,55 @@ +Index: puppetserver/project.clj +=================================================================== +--- puppetserver.orig/project.clj ++++ puppetserver/project.clj +@@ -27,67 +27,53 @@ :min-lein-version "2.9.1" -- :parent-project {:coords [puppetlabs/clj-parent "5.2.16"] +- :parent-project {:coords [puppetlabs/clj-parent "7.3.1"] - :inherit [:managed-dependencies]} + :dependencies [[org.clojure/clojure "1.x"] - :dependencies [[org.clojure/clojure] + [slingshot "debian"] -+ [clj-commons/clj-yaml "debian"] -+ [org.yaml/snakeyaml "1.x"] + [commons-lang "debian"] + [commons-io "debian"] - -- [slingshot] -- [clj-commons/clj-yaml] -- [org.yaml/snakeyaml] -- [commons-lang] -- [commons-io] ++ + [clj-time "debian"] + [grimradical/clj-semver "debian" :exclusions [org.clojure/clojure]] + [prismatic/schema "debian"] @@ -40,23 +33,16 @@ + [liberator "debian"] + [org.apache.commons/commons-exec "debian"] + [io.dropwizard.metrics/metrics-core "debian"] - -- [clj-time] -- [grimradical/clj-semver "0.3.0" :exclusions [org.clojure/clojure]] -- [prismatic/schema] -- [clj-commons/fs] -- [liberator] -- [org.apache.commons/commons-exec] -- [io.dropwizard.metrics/metrics-core] ++ [org.yaml/snakeyaml "1.x"] ++ + [puppetlabs/jruby-utils "debian"] + [puppetlabs/clj-shell-utils "debian"] + [puppetlabs/trapperkeeper "debian"] -+ [puppetlabs/trapperkeeper-webserver-jetty9 "debian"] ++ [com.puppetlabs/trapperkeeper-webserver-jetty10 "debian"] + [puppetlabs/trapperkeeper-authorization "debian"] + [puppetlabs/trapperkeeper-comidi-metrics "debian"] + [puppetlabs/trapperkeeper-metrics "debian"] + [puppetlabs/trapperkeeper-scheduler "debian"] -+ [org.quartz-scheduler/quartz "2.x" :exclusions [c3p0]] + [puppetlabs/trapperkeeper-status "debian"] + [puppetlabs/trapperkeeper-filesystem-watcher "debian"] + [puppetlabs/kitchensink "debian"] @@ -66,20 +52,34 @@ + [puppetlabs/http-client "debian"] + [puppetlabs/comidi "debian"] + [puppetlabs/i18n "debian"] ++ [puppetlabs/rbac-client "debian"] +- [slingshot] +- [org.yaml/snakeyaml] +- [commons-lang] +- [commons-io] +- +- [clj-time] +- [grimradical/clj-semver "0.3.0" :exclusions [org.clojure/clojure]] +- [prismatic/schema] +- [clj-commons/fs] +- [liberator] +- [org.apache.commons/commons-exec] +- [io.dropwizard.metrics/metrics-core] +- [org.yaml/snakeyaml "2.0"] +- - ;; We do not currently use this dependency directly, but - ;; we have documentation that shows how users can use it to - ;; send their logs to logstash, so we include it in the jar. - [net.logstash.logback/logstash-logback-encoder] -+ [org.bouncycastle/bcpkix "debian"]] - +- - [puppetlabs/jruby-utils] - [puppetlabs/clj-shell-utils] - [puppetlabs/trapperkeeper] -- [puppetlabs/trapperkeeper-webserver-jetty9] +- [com.puppetlabs/trapperkeeper-webserver-jetty10] - [puppetlabs/trapperkeeper-authorization] - [puppetlabs/trapperkeeper-comidi-metrics] -- [puppetlabs/trapperkeeper-metrics] +- [puppetlabs/trapperkeeper-metrics "2.0.1"] - [puppetlabs/trapperkeeper-scheduler] - [puppetlabs/trapperkeeper-status] - [puppetlabs/trapperkeeper-filesystem-watcher] @@ -89,7 +89,9 @@ - [puppetlabs/dujour-version-check] - [puppetlabs/http-client] - [puppetlabs/comidi] -- [puppetlabs/i18n]] +- [puppetlabs/i18n] +- [puppetlabs/rbac-client]] ++ [org.bouncycastle/bcpkix "debian"]] :main puppetlabs.trapperkeeper.main @@ -105,6 +107,7 @@ - ["snapshots" "https://artifactory.delivery.puppetlabs.net/artifactory/clojure-snapshots__local/"]] - - :plugins [[lein-parent "0.3.7"] +- [jonase/eastwood "1.2.2" :exclusions [org.clojure/clojure]] - ;; We have to have this, and it needs to agree with clj-parent - ;; until/unless you can have managed plugin dependencies. - [puppetlabs/i18n "0.9.2" :hooks false]] @@ -114,7 +117,7 @@ :uberjar-name "puppet-server-release.jar" :lein-ezbake {:vars {:user "puppet" -@@ -103,9 +144,6 @@ +@@ -106,9 +92,6 @@ :config-dir "ezbake/config" :system-config-dir "ezbake/system-config"} @@ -124,37 +127,36 @@ ;; By declaring a classifier here and a corresponding profile below we'll get an additional jar ;; during `lein jar` that has all the code in the test/ directory. Downstream projects can then ;; depend on this test jar using a :classifier in their :dependencies to reuse the test utility -@@ -113,17 +151,19 @@ +@@ -116,17 +99,17 @@ :classifiers [["test" :testutils]] :profiles {:defaults {:source-paths ["dev"] - :dependencies [[org.clojure/tools.namespace] -- [puppetlabs/trapperkeeper-webserver-jetty9 :classifier "test"] +- [com.puppetlabs/trapperkeeper-webserver-jetty10 :classifier "test"] - [puppetlabs/trapperkeeper nil :classifier "test" :scope "test"] -- [puppetlabs/trapperkeeper-metrics :classifier "test" :scope "test"] +- [puppetlabs/trapperkeeper-metrics "2.0.1" :classifier "test" :scope "test"] - [puppetlabs/kitchensink nil :classifier "test" :scope "test"] - [ring-basic-authentication] - [ring/ring-mock] - [beckon] -- [lambdaisland/uri "1.4.70"]]} +- [lambdaisland/uri "1.4.70"] +- [puppetlabs/rbac-client :classifier "test" :scope "test"]]} +- :dev-deps {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]} + :dependencies [[org.clojure/tools.namespace "debian"] -+ [puppetlabs/trapperkeeper-webserver-jetty9 "debian" :classifier "test"] ++ [com.puppetlabs/trapperkeeper-webserver-jetty10 "debian" :classifier "test"] + [puppetlabs/trapperkeeper "debian" :classifier "test" :scope "test"] + [puppetlabs/trapperkeeper-metrics "debian" :classifier "test" :scope "test"] + [puppetlabs/kitchensink "debian" :classifier "test" :scope "test"] + [ring-basic-authentication "debian"] + [ring/ring-mock "debian"] + [beckon "debian"] -+ [lambdaisland/uri "debian"]]} -+ -+ - :dev [:defaults -- {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]}] -+ {:dependencies [[org.bouncycastle/bcpkix "debian"]]}] - :fips [:defaults - {:dependencies [[org.bouncycastle/bcpkix-fips] - [org.bouncycastle/bc-fips] -@@ -258,5 +298,5 @@ ++ [lambdaisland/uri "debian"] ++ [puppetlabs/rbac-client "debian" :classifier "test" :scope "test"]]} ++ :dev-deps {:dependencies [[org.bouncycastle/bcpkix "debian"]]} + :dev [:defaults :dev-deps] + :fips-deps {:dependencies [[org.bouncycastle/bcpkix-fips] + [org.bouncycastle/bc-fips] +@@ -268,5 +251,5 @@ :uberjar-merge-with {"locales.clj" [(comp read-string slurp) (fn [new prev] (if (map? prev) [new prev] (conj prev new))) diff -Nru puppetserver-7.9.5/debian/patches/0002-Patch-out-pjstadig-humane-test-output-library.patch puppetserver-8.4.0/debian/patches/0002-Patch-out-pjstadig-humane-test-output-library.patch --- puppetserver-7.9.5/debian/patches/0002-Patch-out-pjstadig-humane-test-output-library.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0002-Patch-out-pjstadig-humane-test-output-library.patch 2024-03-01 23:08:21.000000000 +0000 @@ -9,11 +9,11 @@ project.clj | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) -diff --git a/project.clj b/project.clj -index dd3b14b..49b3903 100644 ---- a/project.clj -+++ b/project.clj -@@ -189,13 +189,7 @@ +Index: puppetserver/project.clj +=================================================================== +--- puppetserver.orig/project.clj ++++ puppetserver/project.clj +@@ -136,13 +136,7 @@ ;; use core.async and need more than eight threads to run ;; properly; this setting overrides the default value. Without ;; it the metrics tests will hang. diff -Nru puppetserver-7.9.5/debian/patches/0003-Add-JRuby-stdlib-libraries-to-ruby-load-path.patch puppetserver-8.4.0/debian/patches/0003-Add-JRuby-stdlib-libraries-to-ruby-load-path.patch --- puppetserver-7.9.5/debian/patches/0003-Add-JRuby-stdlib-libraries-to-ruby-load-path.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0003-Add-JRuby-stdlib-libraries-to-ruby-load-path.patch 2024-03-01 23:08:21.000000000 +0000 @@ -12,11 +12,11 @@ src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) -diff --git a/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj b/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj -index 1b4c1b8..477a2c2 100644 ---- a/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj -+++ b/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj -@@ -66,6 +66,9 @@ +Index: puppetserver/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj +=================================================================== +--- puppetserver.orig/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj ++++ puppetserver/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj +@@ -63,6 +63,9 @@ (def default-vendored-gems-dir "/opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems") @@ -26,7 +26,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Private -@@ -73,7 +76,7 @@ +@@ -70,7 +73,7 @@ "Return a list of ruby LOAD_PATH directories built from the user-configurable ruby-load-path setting of the jruby-puppet configuration." [ruby-load-path :- [schema/Str]] diff -Nru puppetserver-7.9.5/debian/patches/0004-Don-t-mess-with-RUBYLIB-when-calling-autosign-script.patch puppetserver-8.4.0/debian/patches/0004-Don-t-mess-with-RUBYLIB-when-calling-autosign-script.patch --- puppetserver-7.9.5/debian/patches/0004-Don-t-mess-with-RUBYLIB-when-calling-autosign-script.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0004-Don-t-mess-with-RUBYLIB-when-calling-autosign-script.patch 2024-03-01 23:08:21.000000000 +0000 @@ -12,11 +12,11 @@ src/clj/puppetlabs/puppetserver/certificate_authority.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/src/clj/puppetlabs/puppetserver/certificate_authority.clj b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -index 0591258..5fbf3ff 100644 ---- a/src/clj/puppetlabs/puppetserver/certificate_authority.clj -+++ b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -@@ -1134,7 +1134,7 @@ +Index: puppetserver/src/clj/puppetlabs/puppetserver/certificate_authority.clj +=================================================================== +--- puppetserver.orig/src/clj/puppetlabs/puppetserver/certificate_authority.clj ++++ puppetserver/src/clj/puppetlabs/puppetserver/certificate_authority.clj +@@ -1451,7 +1451,7 @@ executable {:args [subject] :in csr-stream diff -Nru puppetserver-7.9.5/debian/patches/0005-Disable-remote-update-check-and-dropsonde-analytics.patch puppetserver-8.4.0/debian/patches/0005-Disable-remote-update-check-and-dropsonde-analytics.patch --- puppetserver-7.9.5/debian/patches/0005-Disable-remote-update-check-and-dropsonde-analytics.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0005-Disable-remote-update-check-and-dropsonde-analytics.patch 2024-03-01 23:08:21.000000000 +0000 @@ -13,10 +13,10 @@ .../puppetlabs/services/analytics/analytics_service_test.clj | 3 ++- 3 files changed, 5 insertions(+), 13 deletions(-) -diff --git a/resources/ext/config/conf.d/puppetserver.conf b/resources/ext/config/conf.d/puppetserver.conf -index 8c3646c..c5f7135 100644 ---- a/resources/ext/config/conf.d/puppetserver.conf -+++ b/resources/ext/config/conf.d/puppetserver.conf +Index: puppetserver/resources/ext/config/conf.d/puppetserver.conf +=================================================================== +--- puppetserver.orig/resources/ext/config/conf.d/puppetserver.conf ++++ puppetserver/resources/ext/config/conf.d/puppetserver.conf @@ -72,12 +72,3 @@ profiler: { # enable or disable profiling for the Ruby code; defaults to 'true'. #enabled: true @@ -30,11 +30,11 @@ - # Defaults to one week. - # interval: 604800 -} -diff --git a/src/clj/puppetlabs/services/analytics/analytics_service.clj b/src/clj/puppetlabs/services/analytics/analytics_service.clj -index 37bec05..e05f8f3 100644 ---- a/src/clj/puppetlabs/services/analytics/analytics_service.clj -+++ b/src/clj/puppetlabs/services/analytics/analytics_service.clj -@@ -22,7 +22,7 @@ +Index: puppetserver/src/clj/puppetlabs/services/analytics/analytics_service.clj +=================================================================== +--- puppetserver.orig/src/clj/puppetlabs/services/analytics/analytics_service.clj ++++ puppetserver/src/clj/puppetlabs/services/analytics/analytics_service.clj +@@ -35,7 +35,7 @@ :artifact-id "puppetserver"}) checkin-interval-millis (* 1000 60 60 24) ; once per day update-server-url (get-in config [:product :update-server-url]) @@ -42,8 +42,8 @@ + check-for-updates (get-in config [:product :check-for-updates] false)] (if check-for-updates (interspaced checkin-interval-millis - (fn [] (version-check/check-for-updates! -@@ -31,12 +31,12 @@ + (fn [] +@@ -49,12 +49,12 @@ (log/info (i18n/trs "Puppet Server Update Service has successfully started and will run in the background")) ;; Configure dropsonde, enabled by default if not specified @@ -53,15 +53,15 @@ dropsonde-interval-millis (* 1000 (get-in config [:dropsonde :interval] (* 60 60 24 7)))] (if dropsonde-enabled -- (interspaced dropsonde-interval-millis #(run-dropsonde config)) +- (interspaced dropsonde-interval-millis #(safe-run-dropsonde config) analytics-service-job-group-id) + (log/info "Not submitting module metrics via Dropsonde -- unsupported on Debian.") (log/info (i18n/trs (str "Not submitting module metrics via Dropsonde -- submission is disabled. " "Enable this feature by setting `dropsonde.enabled` to true in Puppet Server''s config.")))))) - context)) -diff --git a/test/integration/puppetlabs/services/analytics/analytics_service_test.clj b/test/integration/puppetlabs/services/analytics/analytics_service_test.clj -index 4ed7d2e..83b5f51 100644 ---- a/test/integration/puppetlabs/services/analytics/analytics_service_test.clj -+++ b/test/integration/puppetlabs/services/analytics/analytics_service_test.clj + context) +Index: puppetserver/test/integration/puppetlabs/services/analytics/analytics_service_test.clj +=================================================================== +--- puppetserver.orig/test/integration/puppetlabs/services/analytics/analytics_service_test.clj ++++ puppetserver/test/integration/puppetlabs/services/analytics/analytics_service_test.clj @@ -26,7 +26,8 @@ :webserver {:port 8081} :product {:update-server-url "http://notarealurl/" diff -Nru puppetserver-7.9.5/debian/patches/0006-Fix-unknown-symbol-error-in-integration-tests.patch puppetserver-8.4.0/debian/patches/0006-Fix-unknown-symbol-error-in-integration-tests.patch --- puppetserver-7.9.5/debian/patches/0006-Fix-unknown-symbol-error-in-integration-tests.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0006-Fix-unknown-symbol-error-in-integration-tests.patch 2024-03-01 23:08:21.000000000 +0000 @@ -18,11 +18,11 @@ .../puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) -diff --git a/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj b/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj -index e7d03a3..cc9d376 100644 ---- a/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj -+++ b/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj -@@ -123,7 +123,7 @@ +Index: puppetserver/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj +=================================================================== +--- puppetserver.orig/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj ++++ puppetserver/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj +@@ -112,7 +112,7 @@ ;; but we don't actually need any of the puppet config values, so here ;; we just create a mock services that passes through to the normal ;; TK config service to make the tests run faster. @@ -31,7 +31,7 @@ ps-config-protocol/PuppetServerConfigService [[:ConfigService get-config get-in-config]] (get-config [this] (get-config)) -@@ -131,7 +131,7 @@ +@@ -120,7 +120,7 @@ (schema/defn ^:always-validate comidi-handler-service :- (schema/protocol tk-services/ServiceDefinition) [coordinator :- (schema/protocol coordinator/TaskCoordinator)] @@ -40,11 +40,11 @@ [[:WebserverService add-ring-handler] [:RequestHandlerService handle-request]] (start [this context] -diff --git a/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj b/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj -index 460eb9d..11dcd2f 100644 ---- a/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj -+++ b/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj -@@ -440,7 +440,7 @@ +Index: puppetserver/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj +=================================================================== +--- puppetserver.orig/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj ++++ puppetserver/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj +@@ -438,7 +438,7 @@ (finally (jruby-testutils/return-instance jruby-service jruby-instance :metrics-test)))))))) diff -Nru puppetserver-7.9.5/debian/patches/0007-Adapt-JRuby-environment-test-for-Debian.patch puppetserver-8.4.0/debian/patches/0007-Adapt-JRuby-environment-test-for-Debian.patch --- puppetserver-7.9.5/debian/patches/0007-Adapt-JRuby-environment-test-for-Debian.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0007-Adapt-JRuby-environment-test-for-Debian.patch 2024-03-01 23:08:21.000000000 +0000 @@ -15,11 +15,11 @@ .../puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj b/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj -index 11dcd2f..f7f9e63 100644 ---- a/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj -+++ b/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj -@@ -596,7 +596,7 @@ +Index: puppetserver/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj +=================================================================== +--- puppetserver.orig/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj ++++ puppetserver/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj +@@ -593,7 +593,7 @@ ;; However, in order to make this test more robust, these variables ;; are always filtered out. (is (= #{"HOME" "PATH" "GEM_HOME" "GEM_PATH" diff -Nru puppetserver-7.9.5/debian/patches/0008-Adjust-defaults-paths.patch puppetserver-8.4.0/debian/patches/0008-Adjust-defaults-paths.patch --- puppetserver-7.9.5/debian/patches/0008-Adjust-defaults-paths.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0008-Adjust-defaults-paths.patch 2024-03-01 23:08:21.000000000 +0000 @@ -22,10 +22,10 @@ .../services/jruby/jruby_puppet_core_test.clj | 20 ++++++++--------- 10 files changed, 40 insertions(+), 40 deletions(-) -diff --git a/ezbake/config/conf.d/global.conf b/ezbake/config/conf.d/global.conf -index ed76c43..2db9aee 100644 ---- a/ezbake/config/conf.d/global.conf -+++ b/ezbake/config/conf.d/global.conf +Index: puppetserver/ezbake/config/conf.d/global.conf +=================================================================== +--- puppetserver.orig/ezbake/config/conf.d/global.conf ++++ puppetserver/ezbake/config/conf.d/global.conf @@ -1,5 +1,5 @@ global: { # Path to logback logging configuration file; for more @@ -33,10 +33,10 @@ - logging-config: /etc/puppetlabs/puppetserver/logback.xml + logging-config: /etc/puppet/puppetserver/logback.xml } -diff --git a/ezbake/config/conf.d/webserver.conf b/ezbake/config/conf.d/webserver.conf -index 1f541e7..19a757d 100644 ---- a/ezbake/config/conf.d/webserver.conf -+++ b/ezbake/config/conf.d/webserver.conf +Index: puppetserver/ezbake/config/conf.d/webserver.conf +=================================================================== +--- puppetserver.orig/ezbake/config/conf.d/webserver.conf ++++ puppetserver/ezbake/config/conf.d/webserver.conf @@ -1,5 +1,5 @@ webserver: { - access-log-config: /etc/puppetlabs/puppetserver/request-logging.xml @@ -44,10 +44,10 @@ client-auth: want ssl-host: 0.0.0.0 ssl-port: 8140 -diff --git a/ezbake/config/logback.xml b/ezbake/config/logback.xml -index d8a5cee..9d3645c 100644 ---- a/ezbake/config/logback.xml -+++ b/ezbake/config/logback.xml +Index: puppetserver/ezbake/config/logback.xml +=================================================================== +--- puppetserver.orig/ezbake/config/logback.xml ++++ puppetserver/ezbake/config/logback.xml @@ -7,11 +7,11 @@ @@ -76,20 +76,20 @@ 200MB 90 -diff --git a/resources/ext/cli_defaults/cli-defaults.sh.erb b/resources/ext/cli_defaults/cli-defaults.sh.erb -index 240ef5f..70dc7ff 100644 ---- a/resources/ext/cli_defaults/cli-defaults.sh.erb -+++ b/resources/ext/cli_defaults/cli-defaults.sh.erb +Index: puppetserver/resources/ext/cli_defaults/cli-defaults.sh.erb +=================================================================== +--- puppetserver.orig/resources/ext/cli_defaults/cli-defaults.sh.erb ++++ puppetserver/resources/ext/cli_defaults/cli-defaults.sh.erb @@ -1,4 +1,4 @@ -INSTALL_DIR="/opt/puppetlabs/server/apps/<%= EZBake::Config[:real_name] %>" +INSTALL_DIR="/usr/share/puppetserver" if [ -n "$JRUBY_JAR" ]; then echo "Warning: the JRUBY_JAR setting is no longer needed and will be ignored." 1>&2 -diff --git a/resources/ext/config/conf.d/puppetserver.conf b/resources/ext/config/conf.d/puppetserver.conf -index 8c3646c..210a8d2 100644 ---- a/resources/ext/config/conf.d/puppetserver.conf -+++ b/resources/ext/config/conf.d/puppetserver.conf +Index: puppetserver/resources/ext/config/conf.d/puppetserver.conf +=================================================================== +--- puppetserver.orig/resources/ext/config/conf.d/puppetserver.conf ++++ puppetserver/resources/ext/config/conf.d/puppetserver.conf @@ -2,16 +2,16 @@ jruby-puppet: { # Where the puppet-agent dependency places puppet, facter, etc... @@ -145,10 +145,10 @@ # (optional) maximum number of JRuby instances to allow #max-active-instances: 1 -diff --git a/resources/ext/config/request-logging.xml b/resources/ext/config/request-logging.xml -index 46fff77..9fa05ce 100644 ---- a/resources/ext/config/request-logging.xml -+++ b/resources/ext/config/request-logging.xml +Index: puppetserver/resources/ext/config/request-logging.xml +=================================================================== +--- puppetserver.orig/resources/ext/config/request-logging.xml ++++ puppetserver/resources/ext/config/request-logging.xml @@ -1,10 +1,10 @@ @@ -162,11 +162,11 @@ 200MB 90 -diff --git a/src/clj/puppetlabs/puppetserver/certificate_authority.clj b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -index 5fbf3ff..97da43c 100644 ---- a/src/clj/puppetlabs/puppetserver/certificate_authority.clj -+++ b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -@@ -701,7 +701,7 @@ +Index: puppetserver/src/clj/puppetlabs/puppetserver/certificate_authority.clj +=================================================================== +--- puppetserver.orig/src/clj/puppetlabs/puppetserver/certificate_authority.clj ++++ puppetserver/src/clj/puppetlabs/puppetserver/certificate_authority.clj +@@ -995,7 +995,7 @@ if it exists. Does nothing if set to a custom value." [cadir] (let [[_ base] (re-matches #"(.*)puppetserver/ca" cadir) @@ -175,11 +175,11 @@ (when base (when (fs/exists? old-cadir) (fs/delete-dir old-cadir)) -diff --git a/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj b/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj -index 477a2c2..3072981 100644 ---- a/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj -+++ b/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj -@@ -49,22 +49,22 @@ +Index: puppetserver/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj +=================================================================== +--- puppetserver.orig/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj ++++ puppetserver/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj +@@ -46,22 +46,22 @@ true) (def default-server-conf-dir @@ -208,11 +208,11 @@ (def debian-jruby-stdlib-dir "/usr/share/jruby/lib/ruby/stdlib") -diff --git a/src/ruby/puppetserver-lib/puppet/server/master.rb b/src/ruby/puppetserver-lib/puppet/server/master.rb -index bbaad9a..875e964 100644 ---- a/src/ruby/puppetserver-lib/puppet/server/master.rb -+++ b/src/ruby/puppetserver-lib/puppet/server/master.rb -@@ -209,7 +209,7 @@ class Puppet::Server::Master +Index: puppetserver/src/ruby/puppetserver-lib/puppet/server/master.rb +=================================================================== +--- puppetserver.orig/src/ruby/puppetserver-lib/puppet/server/master.rb ++++ puppetserver/src/ruby/puppetserver-lib/puppet/server/master.rb +@@ -205,7 +205,7 @@ class Puppet::Server::Master urge_to_migrate = <<-UTM The cadir is currently configured to be inside the #{Puppet[:ssldir]} directory. This config setting and the directory location will not be used in a future version of puppet. Please run the @@ -221,11 +221,11 @@ directory. Use `puppetserver ca migrate --help` for more info. UTM Puppet.warn_once('deprecations', -diff --git a/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj b/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj -index 3b402a3..93ec9ba 100644 ---- a/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj -+++ b/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj -@@ -79,11 +79,11 @@ +Index: puppetserver/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj +=================================================================== +--- puppetserver.orig/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj ++++ puppetserver/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj +@@ -76,11 +76,11 @@ (testing "jruby-puppet values are set to defaults if not provided" (let [initialized-config (jruby-puppet-core/initialize-puppet-config {} {} false)] @@ -242,7 +242,7 @@ (is (= false (:disable-i18n initialized-config))) (is (= true (:http-client-metrics-enabled initialized-config))))) -@@ -94,8 +94,8 @@ +@@ -91,8 +91,8 @@ :master-code-dir "/etc/puppetlabs/puppetserver/code" :master-log-dir "/log/foo"} false)] @@ -253,7 +253,7 @@ (is (= "/etc/puppetlabs/puppetserver" (:server-conf-dir initialized-config))) (is (= "/log/foo" (:server-log-dir initialized-config))) (is (= "/etc/puppetlabs/puppetserver/code" (:server-code-dir initialized-config))))) -@@ -109,8 +109,8 @@ +@@ -106,8 +106,8 @@ (is (= "/my/master/conf" (:server-conf-dir initialized-config))) (is (= "/my/server/code" (:server-code-dir initialized-config))) (is (= "/my/server/code" (:master-code-dir initialized-config))) @@ -264,7 +264,7 @@ (deftest create-jruby-config-test (testing "provided values are not overriden" -@@ -158,5 +158,5 @@ +@@ -155,5 +155,5 @@ (is (= 0 (:max-borrows-per-instance initialized-jruby-config)))) (testing "gem-path defaults to gem-home plus the vendored gems dir if not provided" diff -Nru puppetserver-7.9.5/debian/patches/0009-Remove-call-to-symlink-cadir.patch puppetserver-8.4.0/debian/patches/0009-Remove-call-to-symlink-cadir.patch --- puppetserver-7.9.5/debian/patches/0009-Remove-call-to-symlink-cadir.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0009-Remove-call-to-symlink-cadir.patch 2024-03-01 23:08:21.000000000 +0000 @@ -18,11 +18,11 @@ .../puppetserver/certificate_authority_test.clj | 21 --------------------- 2 files changed, 1 insertion(+), 23 deletions(-) -diff --git a/src/clj/puppetlabs/puppetserver/certificate_authority.clj b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -index 97da43c..46429f4 100644 ---- a/src/clj/puppetlabs/puppetserver/certificate_authority.clj -+++ b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -@@ -759,8 +759,7 @@ +Index: puppetserver/src/clj/puppetlabs/puppetserver/certificate_authority.clj +=================================================================== +--- puppetserver.orig/src/clj/puppetlabs/puppetserver/certificate_authority.clj ++++ puppetserver/src/clj/puppetlabs/puppetserver/certificate_authority.clj +@@ -1053,8 +1053,7 @@ (write-private-key private-key (:cakey ca-settings)) (write-cert cacert (:cacert ca-settings)) (write-crl cacrl (:cacrl ca-settings)) @@ -32,11 +32,11 @@ (schema/defn split-hostnames :- (schema/maybe [schema/Str]) "Given a comma-separated list of hostnames, return a list of the -diff --git a/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj b/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj -index f1ce894..7df5e75 100644 ---- a/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj -+++ b/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj -@@ -174,27 +174,6 @@ +Index: puppetserver/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj +=================================================================== +--- puppetserver.orig/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj ++++ puppetserver/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj +@@ -185,27 +185,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Tests @@ -47,7 +47,7 @@ - ssldir (str tmpdir "puppet/ssl")] - (fs/mkdirs cadir) - (fs/mkdirs ssldir) -- (symlink-cadir cadir) +- (ca/symlink-cadir cadir) - (is (not (fs/exists? (str ssldir "/ca")))))) - (testing "symlinks correctly and removes existing old-cadir if needed" - (let [tmpdir (ks/temp-dir) @@ -56,7 +56,7 @@ - old-cadir (str ssldir "/ca")] - (fs/mkdirs ssldir) - (fs/mkdirs cadir) -- (symlink-cadir cadir) +- (ca/symlink-cadir cadir) - (is (fs/link? old-cadir)) - (let [target (-> old-cadir fs/read-sym-link str)] - (is (= target cadir)))))) @@ -64,3 +64,10 @@ (deftest validate-settings-test (testing "invalid ca-ttl is rejected" (let [settings (assoc +@@ -2414,4 +2393,4 @@ + (Files/createFile (.resolve path-to-file i) default-permissions))) + (let [result (ca/get-paths-to-all-certificate-requests (.toString temp-directory)) + file-names (set (common/extract-file-names-from-paths result))] +- (is (= (set file-names) all-pem-file-names)))))) +\ No newline at end of file ++ (is (= (set file-names) all-pem-file-names)))))) diff -Nru puppetserver-7.9.5/debian/patches/0010-Backport-fix-for-CVE-2023-1894.patch puppetserver-8.4.0/debian/patches/0010-Backport-fix-for-CVE-2023-1894.patch --- puppetserver-7.9.5/debian/patches/0010-Backport-fix-for-CVE-2023-1894.patch 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/0010-Backport-fix-for-CVE-2023-1894.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,127 +0,0 @@ -From: =?utf-8?b?SsOpcsO0bWUgQ2hhcmFvdWk=?= -Date: Sun, 7 May 2023 11:00:09 -0400 -Subject: Backport fix for CVE-2023-1894 - -Forwarded: not-needed -Bug: https://tickets.puppetlabs.com/browse/PE-35786 -Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1035541 -Origin: - commit, https://github.com/puppetlabs/puppetserver/commit/9e0239c19bc852b98c1a63fb33998de7eae388dc - backport, https://github.com/puppetlabs/puppetserver/commit/545998b71baf70e35dc60c287f2cb2fc11ef9be2 ---- - .../puppetserver/certificate_authority.clj | 33 +++++++++++++++++--- - .../puppetserver/certificate_authority_test.clj | 36 ++++++++++++++-------- - 2 files changed, 52 insertions(+), 17 deletions(-) - -diff --git a/src/clj/puppetlabs/puppetserver/certificate_authority.clj b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -index 46429f4..16ab834 100644 ---- a/src/clj/puppetlabs/puppetserver/certificate_authority.clj -+++ b/src/clj/puppetlabs/puppetserver/certificate_authority.clj -@@ -787,6 +787,11 @@ - (utils/subject-alt-names {:dns-name (conj default-alt-names host-name)} false) - (utils/subject-alt-names (update alt-names-list :dns-name conj host-name) false)))) - -+ -+(def pattern-match-dot #"\.") -+(def pattern-starts-with-alphanumeric-or-underscore #"^[\p{Alnum}_].*") -+(def pattern-matches-alphanumeric-with-symbols-string #"^[\p{Alnum}\-_]*[\p{Alnum}_]$") -+ - (schema/defn validate-subject! - "Validate the CSR or certificate's subject name. The subject name must: - * match the hostname specified in the HTTP request (the `subject` parameter) -@@ -795,12 +800,16 @@ - * not contain the wildcard character (*)" - [hostname :- schema/Str - subject :- schema/Str] -+ (log/debug (i18n/trs "Checking \"{0}\" for validity" subject)) -+ - (when-not (= hostname subject) -+ (log/infof "Rejecting subject \"%s\" because it doesn't match hostname \"%s\"" subject hostname) - (sling/throw+ - {:kind :hostname-mismatch -- :msg (i18n/tru "Instance name \"{0}\" does not match requested key \"{1}\"" subject hostname)})) -+ :msg (format "Instance name \"%s\" does not match requested key \"%s\"" subject hostname)})) - - (when (contains-uppercase? hostname) -+ (log/info (i18n/tru "Rejecting subject \"{0}\" because all characters must be lowercase" subject)) - (sling/throw+ - {:kind :invalid-subject-name - :msg (i18n/tru "Certificate names must be lower case.")})) -@@ -809,11 +818,25 @@ - (sling/throw+ - {:kind :invalid-subject-name - :msg (i18n/tru "Subject contains a wildcard, which is not allowed: {0}" subject)})) -- -- (when-not (re-matches #"^([a-z0-9](?:(?:[a-z0-9\-_]*|(? +Forwarded: not-needed + +The only version of Jetty available in Debian, for now, is Jetty9. However +because of its EOL status, its unlikely to be shipped with trixie (see #1055382) +so once a new version of Jetty is packaged, this patch should be dropped. +Index: puppetserver/dev/bootstrap.cfg +=================================================================== +--- puppetserver.orig/dev/bootstrap.cfg ++++ puppetserver/dev/bootstrap.cfg +@@ -2,7 +2,7 @@ puppetlabs.services.request-handler.requ + puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service + puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service + puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service +-puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service ++puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service + puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service + puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service + puppetlabs.services.master.master-service/master-service +Index: puppetserver/dev/dev_tools.clj +=================================================================== +--- puppetserver.orig/dev/dev_tools.clj ++++ puppetserver/dev/dev_tools.clj +@@ -1,5 +1,5 @@ + (ns dev-tools +- (:require [puppetlabs.trapperkeeper.services.webserver.jetty10-service :refer [jetty10-service]] ++ (:require [puppetlabs.trapperkeeper.services.webserver.jetty9-service :refer [jetty9-service]] + + [puppetlabs.trapperkeeper.services.webrouting.webrouting-service :refer [webrouting-service]] + [puppetlabs.services.master.master-service :refer [master-service]] +Index: puppetserver/ext/thread_test/bootstrap.cfg +=================================================================== +--- puppetserver.orig/ext/thread_test/bootstrap.cfg ++++ puppetserver/ext/thread_test/bootstrap.cfg +@@ -2,7 +2,7 @@ puppetlabs.services.request-handler.requ + puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service + puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service + puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service +-puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service ++puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service + puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service + puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service + puppetlabs.services.master.master-service/master-service +Index: puppetserver/ezbake/system-config/services.d/bootstrap.cfg +=================================================================== +--- puppetserver.orig/ezbake/system-config/services.d/bootstrap.cfg ++++ puppetserver/ezbake/system-config/services.d/bootstrap.cfg +@@ -2,7 +2,7 @@ puppetlabs.services.request-handler.requ + puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service + puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service + puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service +-puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service ++puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service + puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service + puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service + puppetlabs.services.master.master-service/master-service +Index: puppetserver/project.clj +=================================================================== +--- puppetserver.orig/project.clj ++++ puppetserver/project.clj +@@ -45,7 +45,7 @@ + [puppetlabs/jruby-utils "debian"] + [puppetlabs/clj-shell-utils "debian"] + [puppetlabs/trapperkeeper "debian"] +- [com.puppetlabs/trapperkeeper-webserver-jetty10 "debian"] ++ [puppetlabs/trapperkeeper-webserver-jetty9 "debian"] + [puppetlabs/trapperkeeper-authorization "debian"] + [puppetlabs/trapperkeeper-comidi-metrics "debian"] + [puppetlabs/trapperkeeper-metrics "debian"] +@@ -100,7 +100,7 @@ + + :profiles {:defaults {:source-paths ["dev"] + :dependencies [[org.clojure/tools.namespace "debian"] +- [com.puppetlabs/trapperkeeper-webserver-jetty10 "debian" :classifier "test"] ++ [puppetlabs/trapperkeeper-webserver-jetty9 "debian" :classifier "test"] + [puppetlabs/trapperkeeper "debian" :classifier "test" :scope "test"] + [puppetlabs/trapperkeeper-metrics "debian" :classifier "test" :scope "test"] + [puppetlabs/kitchensink "debian" :classifier "test" :scope "test"] +@@ -148,18 +148,18 @@ + [org.bouncycastle/bcpkix-jdk18on] + [puppetlabs/jruby-utils] + [puppetlabs/puppetserver ~ps-version] +- [com.puppetlabs/trapperkeeper-webserver-jetty10] ++ [com.puppetlabs/trapperkeeper-webserver-jetty9] + [puppetlabs/trapperkeeper-metrics "2.0.1"]] + :plugins [[puppetlabs/lein-ezbake "2.5.5"]] + :name "puppetserver"} + :uberjar {:dependencies [[org.bouncycastle/bcpkix-jdk18on] +- [com.puppetlabs/trapperkeeper-webserver-jetty10]] ++ [com.puppetlabs/trapperkeeper-webserver-jetty9]] + :aot [puppetlabs.trapperkeeper.main + puppetlabs.trapperkeeper.services.status.status-service + puppetlabs.trapperkeeper.services.metrics.metrics-service + puppetlabs.services.protocols.jruby-puppet + puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service +- puppetlabs.trapperkeeper.services.webserver.jetty10-service ++ puppetlabs.trapperkeeper.services.webserver.jetty9-service + puppetlabs.trapperkeeper.services.webrouting.webrouting-service + puppetlabs.services.legacy-routes.legacy-routes-core + puppetlabs.services.protocols.jruby-metrics +Index: puppetserver/test/integration/puppetlabs/services/master/environment_classes_int_test.clj +=================================================================== +--- puppetserver.orig/test/integration/puppetlabs/services/master/environment_classes_int_test.clj ++++ puppetserver/test/integration/puppetlabs/services/master/environment_classes_int_test.clj +@@ -7,7 +7,7 @@ + [puppetlabs.puppetserver.testutils :as testutils] + [puppetlabs.trapperkeeper.app :as tk-app] + [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-bootstrap-testutils] +- [puppetlabs.trapperkeeper.testutils.webserver :as jetty10] ++ [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] + [puppetlabs.services.master.master-core :as master-core] + [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] + [cheshire.core :as cheshire] +@@ -615,7 +615,7 @@ + (apply str (repeat body-length "a")) + expected-etag)) + (master-core/wrap-with-cache-check jruby-service))] +- (jetty10/with-test-webserver ++ (jetty9/with-test-webserver + app + port + (let [request-url (str "http://localhost:" port) +Index: puppetserver/test/integration/puppetlabs/services/master/environment_transports_int_test.clj +=================================================================== +--- puppetserver.orig/test/integration/puppetlabs/services/master/environment_transports_int_test.clj ++++ puppetserver/test/integration/puppetlabs/services/master/environment_transports_int_test.clj +@@ -6,7 +6,7 @@ + [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] + [puppetlabs.puppetserver.testutils :as testutils] + [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-bootstrap-testutils] +- [puppetlabs.trapperkeeper.testutils.webserver :as jetty10] ++ [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] + [puppetlabs.services.master.master-core :as master-core] + [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] + [cheshire.core :as json] +@@ -485,7 +485,7 @@ Puppet::ResourceApi.register_transport( + (apply str (repeat body-length "a")) + expected-etag)) + (master-core/wrap-with-cache-check jruby-service))] +- (jetty10/with-test-webserver ++ (jetty9/with-test-webserver + app + port + (let [request-url (str "http://localhost:" port) +Index: puppetserver/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj +=================================================================== +--- puppetserver.orig/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj ++++ puppetserver/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj +@@ -8,7 +8,7 @@ + (java.util.zip GZIPInputStream)) + (:require [clojure.test :refer [deftest is testing use-fixtures]] + [puppetlabs.trapperkeeper.testutils.logging :as logutils] +- [puppetlabs.trapperkeeper.testutils.webserver :as jetty10] ++ [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] + [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-puppet-testutils] + [ring.middleware.basic-authentication :as auth] + [schema.core :as schema] +@@ -170,7 +170,7 @@ + (use-fixtures :once http-client-scripting-container-fixture) + + (deftest test-ruby-http-client +- (jetty10/with-test-webserver ring-app port ++ (jetty9/with-test-webserver ring-app port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -180,7 +180,7 @@ + (is (= "hi" (.runScriptlet sc (format "$c.post(URI('%s'), 'foo').body" url)))))))))) + + (deftest http-escaped-urls-test +- (jetty10/with-test-webserver ring-app port ++ (jetty9/with-test-webserver ring-app port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -190,7 +190,7 @@ + (is (= (str url "/a%20b%3Fc?foo=bar") (.runScriptlet sc "$response.url.to_s"))))))))) + + (deftest http-basic-auth +- (jetty10/with-test-webserver ring-app-with-auth port ++ (jetty9/with-test-webserver ring-app-with-auth port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -213,7 +213,7 @@ + (is (= "access denied" (.runScriptlet sc "$response.body"))))))))) + + (deftest http-compressed-requests +- (jetty10/with-test-webserver ring-app-decompressing-gzipped-request port ++ (jetty9/with-test-webserver ring-app-decompressing-gzipped-request port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -244,7 +244,7 @@ + + (defmacro with-webserver-with-protocols + [protocols cipher-suites & body] +- `(jetty10/with-test-webserver-and-config ring-app port# ++ `(jetty9/with-test-webserver-and-config ring-app port# + (merge {:ssl-host "localhost" + :ssl-port 10080 + :ssl-ca-cert ca-pem +@@ -304,7 +304,7 @@ + (deftest clients-persist + (testing "client persists when making HTTP requests" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app port ++ (jetty9/with-test-webserver ring-app port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port) +@@ -313,7 +313,7 @@ + (is (= client1 client2)))))))) + (testing "all instances of HttpClient have the same underlying client object" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app port ++ (jetty9/with-test-webserver ring-app port + (with-scripting-container sc + (with-http-client sc {} + (let [client1 (.runScriptlet sc "$c.class.client") +@@ -324,7 +324,7 @@ + (deftest connections-closed + (testing "connection header always set to close on get" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app-connection-closed port ++ (jetty9/with-test-webserver ring-app-connection-closed port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -332,7 +332,7 @@ + (.runScriptlet sc (format "$c.get(URI('%s')).body" url)))))))))) + (testing "connection header always set to close on post" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app-connection-closed port ++ (jetty9/with-test-webserver ring-app-connection-closed port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -340,7 +340,7 @@ + (.runScriptlet sc (format "$c.post(URI('%s'), 'foo').body" url)))))))))) + (testing "client's terminate function closes the client" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app-connection-closed port ++ (jetty9/with-test-webserver ring-app-connection-closed port + (with-scripting-container sc + (with-http-client sc {} + (let [url (str "http://localhost:" port)] +@@ -358,7 +358,7 @@ + (deftest http-and-https + (testing "can make http calls after https calls without a new scripting container" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app-alternate port ++ (jetty9/with-test-webserver ring-app-alternate port + (with-webserver-with-protocols nil nil + (with-scripting-container sc + (with-http-client sc {} +@@ -373,7 +373,7 @@ + + (testing "can make https calls after http calls without a new scripting container" + (logutils/with-test-logging +- (jetty10/with-test-webserver ring-app-alternate port ++ (jetty9/with-test-webserver ring-app-alternate port + (with-webserver-with-protocols nil nil + (with-scripting-container sc + (with-http-client sc {} +Index: puppetserver/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj +=================================================================== +--- puppetserver.orig/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj ++++ puppetserver/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj +@@ -13,7 +13,7 @@ + [puppetlabs.trapperkeeper.services.metrics.metrics-service :as metrics] + [puppetlabs.services.jruby.jruby-puppet-service :as jruby-puppet] + [puppetlabs.services.puppet-profiler.puppet-profiler-service :as profiler] +- [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10-service] ++ [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9-service] + [puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service :as jruby-pool-manager] + [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as scheduler-service] + [puppetlabs.trapperkeeper.services.status.status-service :as status-service] +@@ -46,7 +46,7 @@ + metrics/metrics-service + scheduler-service/scheduler-service + status-service/status-service +- jetty10-service/jetty10-service ++ jetty9-service/jetty9-service + webrouting-service/webrouting-service]) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Index: puppetserver/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj +=================================================================== +--- puppetserver.orig/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj ++++ puppetserver/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj +@@ -2,7 +2,7 @@ + (:import (com.puppetlabs.puppetserver PuppetProfiler)) + (:require [clojure.test :refer [deftest is testing]] + [puppetlabs.services.puppet-profiler.puppet-profiler-service :refer [puppet-profiler-service]] +- [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10-service] ++ [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9-service] + [puppetlabs.trapperkeeper.services.metrics.metrics-service :as metrics-service] + [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as scheduler-service] + [puppetlabs.trapperkeeper.services.status.status-service :as status-service] +@@ -16,7 +16,7 @@ + (bootstrap/with-app-with-config + app + [puppet-profiler-service +- jetty10-service/jetty10-service ++ jetty9-service/jetty9-service + metrics-service/metrics-service + scheduler-service/scheduler-service + status-service/status-service +Index: puppetserver/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj +=================================================================== +--- puppetserver.orig/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj ++++ puppetserver/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj +@@ -16,7 +16,7 @@ + [puppetlabs.puppetserver.bootstrap-testutils :as jruby-bootstrap] + [puppetlabs.services.protocols.versioned-code :as vc] + [puppetlabs.services.puppet-profiler.puppet-profiler-service :as profiler] +- [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10] ++ [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] + [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as tk-scheduler] + [puppetlabs.services.request-handler.request-handler-service :as handler-service] + [puppetlabs.services.config.puppet-server-config-service :as ps-config] +@@ -325,7 +325,7 @@ + profiler/puppet-profiler-service + handler-service/request-handler-service + ps-config/puppet-server-config-service +- jetty10/jetty10-service ++ jetty9/jetty9-service + ca-service/certificate-authority-service + authorization-service/authorization-service + routing-service/webrouting-service diff -Nru puppetserver-7.9.5/debian/patches/resolve-ftbfs-with-java-21.patch puppetserver-8.4.0/debian/patches/resolve-ftbfs-with-java-21.patch --- puppetserver-7.9.5/debian/patches/resolve-ftbfs-with-java-21.patch 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/resolve-ftbfs-with-java-21.patch 2024-03-01 23:08:21.000000000 +0000 @@ -0,0 +1,20 @@ +Description: Resolve FTBFS with Java 21 +Author: Jérôme Charaoui +Forwarded: not-needed + +This removes an exception from project.clj's :fips profile which throws an +exception when Java 21 is used, regardless of whether the profile is activated +or not. +Index: puppetserver/project.clj +=================================================================== +--- puppetserver.orig/project.clj ++++ puppetserver/project.clj +@@ -125,7 +125,7 @@ + (throw unsupported-ex)) + 11 ["-Djava.security.properties==./dev-resources/java.security.jdk11on-fips"] + 17 ["-Djava.security.properties==./dev-resources/java.security.jdk11on-fips"] +- (throw unsupported-ex)))} ++ (do)))} + :fips [:defaults :fips-deps] + + :testutils {:source-paths ["test/unit" "test/integration"]} diff -Nru puppetserver-7.9.5/debian/patches/series puppetserver-8.4.0/debian/patches/series --- puppetserver-7.9.5/debian/patches/series 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/patches/series 2024-03-01 23:08:21.000000000 +0000 @@ -7,4 +7,5 @@ 0007-Adapt-JRuby-environment-test-for-Debian.patch 0008-Adjust-defaults-paths.patch 0009-Remove-call-to-symlink-cadir.patch -0010-Backport-fix-for-CVE-2023-1894.patch +downgrade-to-jetty9.patch +resolve-ftbfs-with-java-21.patch diff -Nru puppetserver-7.9.5/debian/puppetserver.README.source puppetserver-8.4.0/debian/puppetserver.README.source --- puppetserver-7.9.5/debian/puppetserver.README.source 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/puppetserver.README.source 2024-03-01 23:08:21.000000000 +0000 @@ -12,7 +12,6 @@ generated using: gbp export-orig - gbp export-orig --component rubygem-concurrent-ruby 2. Ruby gems ------------ @@ -24,14 +23,6 @@ environment. As such, if any of the packaged Ruby gem dependencies are updated in Debian, the puppetserver package will require a rebuild. -The only exception is the concurrent-ruby gem. Puppet Server requires the JRuby -bindings for this gem, which the current packaging in Debian does not provide. -The work to fix this is being tracked in bug #1029139. - -As a workaround, the package is bundling this gem from rubygems.org as an extra -component tarball. The debian/gem-download.sh script is provided in the source -package for this purpose. - 3. AOT compilation ------------------ diff -Nru puppetserver-7.9.5/debian/puppetserver.default puppetserver-8.4.0/debian/puppetserver.default --- puppetserver-7.9.5/debian/puppetserver.default 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/puppetserver.default 2024-03-01 23:08:21.000000000 +0000 @@ -15,3 +15,7 @@ INSTALL_DIR="/usr/share/puppetserver" CONFIG="/etc/puppet/puppetserver/conf.d" BOOTSTRAP_CONFIG="/etc/puppet/puppetserver/services.d" + +# Prevent issue with non-C/English locales +# (see Debian bug #1063568) +LANG="C" diff -Nru puppetserver-7.9.5/debian/puppetserver.lintian-overrides puppetserver-8.4.0/debian/puppetserver.lintian-overrides --- puppetserver-7.9.5/debian/puppetserver.lintian-overrides 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/puppetserver.lintian-overrides 2024-03-01 23:08:21.000000000 +0000 @@ -1,8 +1,2 @@ -# acceptable for bundled gem -extra-license-file [usr/lib/puppetserver/vendored-jruby-gems/gems/concurrent-ruby-*/LICENSE.md] -jar-not-in-usr-share [usr/lib/puppetserver/vendored-jruby-gems/gems/concurrent-ruby-*/lib/concurrent/concurrent_ruby.jar] -package-contains-documentation-outside-usr-share-doc [usr/lib/puppetserver/vendored-jruby-gems/gems/concurrent-ruby-*/*.md] # these source files are believed to be necessary jar-contains-source com/puppetlabs/puppetserver/*.java [usr/share/puppetserver/puppetserver-*.jar] -# patches welcome -package-supports-alternative-init-but-no-init.d-script [lib/systemd/system/puppetserver.service] diff -Nru puppetserver-7.9.5/debian/rules puppetserver-8.4.0/debian/rules --- puppetserver-7.9.5/debian/rules 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/rules 2024-03-01 23:08:21.000000000 +0000 @@ -7,12 +7,12 @@ export LEIN_OFFLINE=true NAME=puppetserver -DEBIAN_GEMS=deep_merge fast_gettext hocon gettext locale puppetserver-ca text semantic_puppet +DEBIAN_GEMS=concurrent-ruby deep_merge fast_gettext hocon gettext locale puppetserver-ca text semantic_puppet # used to generate dependencies on the upstream versions of these packages -RUBY_PACKAGES=ruby-deep-merge ruby-fast-gettext ruby-gettext ruby-hocon ruby-locale ruby-puppetserver-ca-cli ruby-semantic-puppet ruby-text +RUBY_PACKAGES=ruby-concurrent ruby-deep-merge ruby-fast-gettext ruby-gettext ruby-hocon ruby-locale ruby-puppetserver-ca-cli ruby-semantic-puppet ruby-text -CLASSPATH=/usr/share/java/clojure.jar:/usr/share/java/slingshot.jar:/usr/share/java/clj-yaml.jar:/usr/share/java/snakeyaml-1.x.jar:/usr/share/java/commons-lang.jar:/usr/share/java/commons-io.jar:/usr/share/java/clj-time.jar:/usr/share/java/clj-semver.jar:/usr/share/java/schema.jar:/usr/share/java/fs.jar:/usr/share/java/liberator.jar:/usr/share/java/commons-exec.jar:/usr/share/java/metrics-core.jar:/usr/share/java/jruby-utils.jar:/usr/share/java/clj-shell-utils.jar:/usr/share/java/trapperkeeper.jar:/usr/share/java/trapperkeeper-status.jar:/usr/share/java/trapperkeeper-scheduler.jar:/usr/share/java/trapperkeeper-webserver-jetty9.jar:/usr/share/java/trapperkeeper-authorization.jar:/usr/share/java/trapperkeeper-comidi-metrics.jar:/usr/share/java/trapperkeeper-metrics.jar:/usr/share/java/trapperkeeper-filesystem-watcher.jar:/usr/share/java/kitchensink.jar:/usr/share/java/ssl-utils.jar:/usr/share/java/ring-middleware.jar:/usr/share/java/dujour-version-check.jar:/usr/share/java/http-client.jar:/usr/share/java/comidi.jar:/usr/share/java/i18n.jar:/usr/share/java/semver.jar:/usr/share/java/libtrapperkeeper-comidi-metrics-clojure.jar +CLASSPATH=/usr/share/java/clojure.jar:/usr/share/java/slingshot.jar:/usr/share/java/snakeyaml-1.x.jar:/usr/share/java/commons-lang.jar:/usr/share/java/commons-io.jar:/usr/share/java/clj-time.jar:/usr/share/java/clj-semver.jar:/usr/share/java/schema.jar:/usr/share/java/fs.jar:/usr/share/java/liberator.jar:/usr/share/java/commons-exec.jar:/usr/share/java/metrics-core.jar:/usr/share/java/jruby-utils.jar:/usr/share/java/clj-shell-utils.jar:/usr/share/java/trapperkeeper.jar:/usr/share/java/trapperkeeper-status.jar:/usr/share/java/trapperkeeper-scheduler.jar:/usr/share/java/trapperkeeper-webserver-jetty9.jar:/usr/share/java/trapperkeeper-authorization.jar:/usr/share/java/trapperkeeper-comidi-metrics.jar:/usr/share/java/trapperkeeper-metrics.jar:/usr/share/java/trapperkeeper-filesystem-watcher.jar:/usr/share/java/kitchensink.jar:/usr/share/java/ssl-utils.jar:/usr/share/java/ring-middleware.jar:/usr/share/java/dujour-version-check.jar:/usr/share/java/http-client.jar:/usr/share/java/comidi.jar:/usr/share/java/i18n.jar:/usr/share/java/semver.jar:/usr/share/java/libtrapperkeeper-comidi-metrics-clojure.jar %: dh $@ --with javahelper @@ -26,10 +26,6 @@ lein jar execute_after_dh_auto_build: - # bundle gems shipped as component tarballs - mkdir -p target/vendored-jruby-gems - cp -a rubygem-*/gems target/vendored-jruby-gems - cp -a rubygem-*/specifications target/vendored-jruby-gems # create symlink tree for vendored-jruby-gems mkdir -p target/vendored-jruby-gems/gems mkdir -p target/vendored-jruby-gems/specifications @@ -67,9 +63,9 @@ override_dh_auto_test: ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) - # make hiera, facter and puppet gems available to the testsuite + # make facter and puppet gems available to the testsuite # upstream uses git submodules here, but we can use Debian sources instead - for component in hiera facter puppet resource_api; do \ + for component in facter puppet resource_api; do \ mkdir -p ruby/$${component}/lib; \ if [ "$$component" = "puppet" ]; then package=puppet-agent ; \ elif [ "$$component" = "resource_api" ]; then package=ruby-puppet-resource-api ; \ @@ -124,7 +120,7 @@ ver=$$(dpkg-query -f '$${source:Upstream-Version}' -W $${dep}); \ ruby_depends="$${ruby_depends}, $${dep} (<< $${ver}.), $${dep} (>= $${ver}~~)"; \ done; \ - dh_gencontrol -- -VrubyDepends="$${ruby_depends}" -VclojureAotDepends="$${aot_depends}" + dh_gencontrol -- -VrubyDepends="$${ruby_depends}" override_dh_installman: mkdir debian/tmp/man diff -Nru puppetserver-7.9.5/debian/tests/spec/fixtures/freezer/manifests/site.pp puppetserver-8.4.0/debian/tests/spec/fixtures/freezer/manifests/site.pp --- puppetserver-7.9.5/debian/tests/spec/fixtures/freezer/manifests/site.pp 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/debian/tests/spec/fixtures/freezer/manifests/site.pp 2024-03-01 23:08:21.000000000 +0000 @@ -0,0 +1,3 @@ +node default { + include freezer +} diff -Nru puppetserver-7.9.5/debian/tests/spec/fixtures/freezer/modules/freezer/manifests/init.pp puppetserver-8.4.0/debian/tests/spec/fixtures/freezer/modules/freezer/manifests/init.pp --- puppetserver-7.9.5/debian/tests/spec/fixtures/freezer/modules/freezer/manifests/init.pp 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/debian/tests/spec/fixtures/freezer/modules/freezer/manifests/init.pp 2024-03-01 23:08:21.000000000 +0000 @@ -0,0 +1,9 @@ +class freezer { + file { + '/tmp/freezer': + ensure => directory, + recurse => true, + source => 'puppet:///modules/freezer', + ; + } +} diff -Nru puppetserver-7.9.5/debian/tests/spec/puppetserver/01_service_spec.rb puppetserver-8.4.0/debian/tests/spec/puppetserver/01_service_spec.rb --- puppetserver-7.9.5/debian/tests/spec/puppetserver/01_service_spec.rb 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/tests/spec/puppetserver/01_service_spec.rb 2024-03-01 23:08:21.000000000 +0000 @@ -32,6 +32,12 @@ end end +describe "access log records queries" do + describe file('/var/log/puppetserver/puppetserver-access.log') do + its(:content) { should contain '"GET /status/v1/simple HTTP/1.1" 200' } + end +end + describe "call-home analytics and update checks disabled by default" do describe file('/var/log/puppetserver/puppetserver.log') do its(:content) { should match %r{Not submitting module metrics via Dropsonde -- submission is disabled} } diff -Nru puppetserver-7.9.5/debian/tests/spec/puppetserver/freezer_spec.rb puppetserver-8.4.0/debian/tests/spec/puppetserver/freezer_spec.rb --- puppetserver-7.9.5/debian/tests/spec/puppetserver/freezer_spec.rb 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/debian/tests/spec/puppetserver/freezer_spec.rb 2024-03-01 23:08:21.000000000 +0000 @@ -0,0 +1,31 @@ +require 'spec_helper' +require 'fileutils' +require 'timeout' + +describe "doesn't freeze on particular file resource in non-english locale" do + before(:all) do + # set up test puppet environment + FileUtils.mkdir_p '/etc/puppet/code/environments/production' + FileUtils.cp_r 'spec/fixtures/freezer', '/etc/puppet/code/environments' + + # switch to non-english default locale + File.write('/etc/locale.conf', "LANG=de_CH.UTF-8\n") + File.write('/etc/locale.gen', "de_CH UTF-8\n") + system('locale-gen') + + # restart puppetserver + puts 'restarting Puppet Server...' + system('systemctl daemon-reexec') + system('systemctl restart puppetserver') + end + + around(:each) do |agent| + Timeout::timeout(120) { + agent.run + } + end + + describe command('puppet agent --test --environment=freezer') do + its(:exit_status) { should eq 2 } + end +end diff -Nru puppetserver-7.9.5/debian/tests/spec/puppetserver/gem_spec.rb puppetserver-8.4.0/debian/tests/spec/puppetserver/gem_spec.rb --- puppetserver-7.9.5/debian/tests/spec/puppetserver/gem_spec.rb 2023-05-07 15:09:17.000000000 +0000 +++ puppetserver-8.4.0/debian/tests/spec/puppetserver/gem_spec.rb 2024-03-01 23:08:21.000000000 +0000 @@ -9,7 +9,9 @@ describe command('puppetserver gem list') do its(:exit_status) { should eq 0 } - its(:stdout) { should match(/^cmath \(default: [0-9\.]+\)/) } - its(:stdout) { should match(/^deep_merge \([0-9\.]+\)/) } + # jruby stdlib gem + its(:stdout) { should match(/^ffi \(default: [0-9\.]+ java\)/) } + # vendored gem + its(:stdout) { should match(/^concurrent-ruby \([0-9\.]+\)/) } end end diff -Nru puppetserver-7.9.5/dev/bootstrap.cfg puppetserver-8.4.0/dev/bootstrap.cfg --- puppetserver-7.9.5/dev/bootstrap.cfg 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/dev/bootstrap.cfg 2024-01-15 23:29:55.000000000 +0000 @@ -2,7 +2,7 @@ puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service -puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service +puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service puppetlabs.services.master.master-service/master-service diff -Nru puppetserver-7.9.5/dev/dev_tools.clj puppetserver-8.4.0/dev/dev_tools.clj --- puppetserver-7.9.5/dev/dev_tools.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/dev/dev_tools.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns dev-tools - (:require [puppetlabs.trapperkeeper.services.webserver.jetty9-service :refer [jetty9-service]] + (:require [puppetlabs.trapperkeeper.services.webserver.jetty10-service :refer [jetty10-service]] [puppetlabs.trapperkeeper.services.webrouting.webrouting-service :refer [webrouting-service]] [puppetlabs.services.master.master-service :refer [master-service]] diff -Nru puppetserver-7.9.5/dev/puppetserver.conf puppetserver-8.4.0/dev/puppetserver.conf --- puppetserver-7.9.5/dev/puppetserver.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/dev/puppetserver.conf 2024-01-15 23:29:55.000000000 +0000 @@ -196,4 +196,10 @@ allow-subject-alt-names: false allow-authorization-extensions: false + # Disable auto renewal of certs by default. + allow-auto-renewal: false + # This value determines the lifetime of the cert if auto-renewal is enabled + auto-renewal-cert-ttl: "90d" + # Default cert expiration time. If the value is set here, it will take precedence over ca-ttl setting in puppet.conf + #ca-ttl: "60d" } diff -Nru puppetserver-7.9.5/dev-resources/puppetlabs/puppetserver/certificate_authority_test/pems/bundle/ca/ca_pub.pem puppetserver-8.4.0/dev-resources/puppetlabs/puppetserver/certificate_authority_test/pems/bundle/ca/ca_pub.pem --- puppetserver-7.9.5/dev-resources/puppetlabs/puppetserver/certificate_authority_test/pems/bundle/ca/ca_pub.pem 1970-01-01 00:00:00.000000000 +0000 +++ puppetserver-8.4.0/dev-resources/puppetlabs/puppetserver/certificate_authority_test/pems/bundle/ca/ca_pub.pem 2024-01-15 23:29:55.000000000 +0000 @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAj9E21xV0npvm8tuz5c8B +yDWUz7q6lJA6cmT2ruV3jCPdm7WdrTQSbIRPHEiG4XhREXdgyGCADbfxo4GOMsPP +qOk5LfMBqZaAc7eD/NShOZVclbPvDk3FVWIdKYTpQuRoNWtDCSiBfz+QJMBbHqTl +hL8tBRM74NO7Hv+qq/HQJtfFe54LWA9mLtKMdUSeuIofiW75wztchVC9AnbyW8Hr +yD58hO7RQcSpjHok0U4jvr8s8dMdLmarPF96ixLHGmGuqIh5n2ns9wCYqvcqIZoO +8S54BkJOKqrWFMnyKnl7pOy5hFYfRatrOEapBrrSMRXdUd+wy2TO6bf5DxlLPhYy +JSBtZp9sMp2RrgmOhqx1vYdRRFKWYKsDGAZDgblfTfzloyeAVr4l/KJGhUh+APDh +w4wHIylRJMa0ky/6Xaave8Vvp/9udSbDnmhE0eVHt1H2Z07f7yn09inbUEjeTu5c +FDzGqoHVa/qD5ymGMIR2TgOgxeU7y2bPBIZl2ufBoExR5nxLd9s/4sdUlPICuIFK +eZW32ed8NA7sUQQf23TnDc0f8L5PB7ksKl6cX6aZz/UJUb5ApZxLu4gqmYASVvOd +e+jydg/sNQsQGPktlntLvfjpxdDDr44eDtj6SIzGclv1yZe0DecJilg9LeaBvnD9 +9FthUwYORZjSlb2yl15/o7cCAwEAAQ== +-----END PUBLIC KEY----- diff -Nru puppetserver-7.9.5/docker/.gitignore puppetserver-8.4.0/docker/.gitignore --- puppetserver-7.9.5/docker/.gitignore 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -TEST-*.xml diff -Nru puppetserver-7.9.5/docker/Gemfile puppetserver-8.4.0/docker/Gemfile --- puppetserver-7.9.5/docker/Gemfile 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/Gemfile 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -source "https://rubygems.org" - -gem 'pupperware', - :git => 'https://github.com/puppetlabs/pupperware.git', - :branch => 'main', - :glob => 'gem/*.gemspec' - -group :test do - gem 'rspec' - gem 'rspec_junit_formatter' -end diff -Nru puppetserver-7.9.5/docker/Makefile puppetserver-8.4.0/docker/Makefile --- puppetserver-7.9.5/docker/Makefile 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -NAMESPACE ?= puppet -git_describe = $(shell git describe) -vcs_ref := $(shell git rev-parse HEAD) -build_date := $(shell date -u +%FT%T) -hadolint_available := $(shell hadolint --help > /dev/null 2>&1; echo $$?) -hadolint_command := hadolint -hadolint_container := ghcr.io/hadolint/hadolint:latest - -export BUNDLE_PATH = $(PWD)/.bundle/gems -export BUNDLE_BIN = $(PWD)/.bundle/bin -export GEMFILE = $(PWD)/Gemfile -export DOCKER_BUILDKIT ?= 1 -PUPPERWARE_ANALYTICS_STREAM ?= dev - -ifeq ($(IS_RELEASE),true) - VERSION ?= $(shell echo $(git_describe) | sed 's/-.*//') - PRODUCT ?= puppetserver - # to work around failures that occur between when the repo is tagged and when the package - # is actually shipped, see if this version exists in dujour - PUBLISHED_VERSION ?= $(shell curl --silent 'https://updates.puppetlabs.com/?product=$(PRODUCT)&version=$(VERSION)' | jq '."version"' | tr -d '"') - # For our containers built from packages, we want those to be built once then never changed - # so check to see if that container already exists on dockerhub - CONTAINER_EXISTS = $(shell DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect $(NAMESPACE)/puppetserver:$(VERSION) > /dev/null 2>&1; echo $$?) -ifeq ($(CONTAINER_EXISTS),0) - SKIP_BUILD ?= true -else ifneq ($(VERSION),$(PUBLISHED_VERSION)) - SKIP_BUILD ?= true -endif - - BUILD_TYPE ?= release - LATEST_VERSION ?= latest -else - BUILD_TYPE ?= edge - VERSION ?= edge - IS_LATEST := false -endif - -prep: - @git fetch --unshallow ||: - @git fetch origin 'refs/tags/*:refs/tags/*' -ifeq ($(SKIP_BUILD),true) - @echo "SKIP_BUILD is true, exiting with 1" - @exit 1 -endif - -lint: -ifeq ($(hadolint_available),0) - @$(hadolint_command) puppetserver/Dockerfile -else - @docker pull $(hadolint_container) - @docker run --rm -v $(PWD)/puppetserver/Dockerfile:/Dockerfile \ - -i $(hadolint_container) $(hadolint_command) Dockerfile -endif - -build: prep - docker buildx build \ - ${DOCKER_BUILD_FLAGS} \ - --load \ - --pull \ - --build-arg build_type=$(BUILD_TYPE) \ - --build-arg vcs_ref=$(vcs_ref) \ - --build-arg build_date=$(build_date) \ - --build-arg version=$(VERSION) \ - --build-arg pupperware_analytics_stream=$(PUPPERWARE_ANALYTICS_STREAM) \ - --file puppetserver/Dockerfile \ - --tag $(NAMESPACE)/puppetserver:$(VERSION) $(PWD)/.. -ifeq ($(IS_LATEST),true) - @docker tag $(NAMESPACE)/puppetserver:$(VERSION) \ - $(NAMESPACE)/puppetserver:$(LATEST_VERSION) -endif - -test: prep - @bundle install --path $$BUNDLE_PATH --gemfile $$GEMFILE --with test - @bundle update - @PUPPET_TEST_DOCKER_IMAGE=$(NAMESPACE)/puppetserver:$(VERSION) \ - bundle exec --gemfile $$GEMFILE \ - rspec --options puppetserver/.rspec spec - -push-image: prep - @docker push puppet/puppetserver:$(VERSION) -ifeq ($(IS_LATEST),true) - @docker push puppet/puppetserver:$(LATEST_VERSION) -endif - -push-readme: - @docker pull sheogorath/readme-to-dockerhub - @docker run --rm \ - -v $(PWD)/puppetserver/README.md:/data/README.md \ - -e DOCKERHUB_USERNAME="$(DOCKERHUB_USERNAME)" \ - -e DOCKERHUB_PASSWORD="$(DOCKERHUB_PASSWORD)" \ - -e DOCKERHUB_REPO_PREFIX=puppet \ - -e DOCKERHUB_REPO_NAME=puppetserver \ - sheogorath/readme-to-dockerhub - -publish: push-image push-readme - -.PHONY: prep lint build test publish push-image push-readme diff -Nru puppetserver-7.9.5/docker/docker-compose.yml puppetserver-8.4.0/docker/docker-compose.yml --- puppetserver-7.9.5/docker/docker-compose.yml 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/docker-compose.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -version: '3.5' - -services: - puppet: - hostname: puppet - image: ${PUPPET_TEST_DOCKER_IMAGE:-puppet/puppetserver} - expose: - - 8141 - environment: - - PUPPETSERVER_HOSTNAME=puppet - - PUPPET_MASTERPORT=8141 - - PUPPERWARE_ANALYTICS_ENABLED=${PUPPERWARE_ANALYTICS_ENABLED:-false} - - USE_PUPPETDB=false - - compiler: - hostname: compiler - image: ${PUPPET_TEST_DOCKER_IMAGE:-puppet/puppetserver} - environment: - - PUPPERWARE_ANALYTICS_ENABLED=${PUPPERWARE_ANALYTICS_ENABLED:-false} - - PUPPETSERVER_HOSTNAME=compiler - - CA_ENABLED=false - - CA_HOSTNAME=puppet - - CA_MASTERPORT=8141 - - USE_PUPPETDB=false - -networks: - default: - name: puppetserver_test diff -Nru puppetserver-7.9.5/docker/puppetserver/.rspec puppetserver-8.4.0/docker/puppetserver/.rspec --- puppetserver-7.9.5/docker/puppetserver/.rspec 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/.rspec 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ ---format RspecJunitFormatter ---out puppetserver/TEST-rspec.xml ---format documentation diff -Nru puppetserver-7.9.5/docker/puppetserver/Dockerfile puppetserver-8.4.0/docker/puppetserver/Dockerfile --- puppetserver-7.9.5/docker/puppetserver/Dockerfile 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/Dockerfile 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -ARG build_type=edge -ARG UBUNTU_CODENAME=bionic - -###################################################### -# base -###################################################### - -FROM ubuntu:18.04 as base - -ARG PACKAGES=ca-certificates\ git -ARG DUMB_INIT_VERSION="1.2.2" - -LABEL org.label-schema.maintainer="Puppet Release Team " \ - org.label-schema.vendor="Puppet" \ - org.label-schema.url="https://github.com/puppetlabs/puppetserver" \ - org.label-schema.license="Apache-2.0" \ - org.label-schema.vcs-url="https://github.com/puppetlabs/puppetserver" \ - org.label-schema.schema-version="1.0" \ - org.label-schema.dockerfile="/Dockerfile" - -ENV PUPPERWARE_ANALYTICS_TRACKING_ID="UA-132486246-4" \ - PUPPERWARE_ANALYTICS_APP_NAME="puppetserver" \ - PUPPERWARE_ANALYTICS_ENABLED=false \ - PUPPETSERVER_JAVA_ARGS="-Xms512m -Xmx512m" \ - PATH=/opt/puppetlabs/server/bin:/opt/puppetlabs/puppet/bin:/opt/puppetlabs/bin:$PATH \ - SSLDIR=/etc/puppetlabs/puppet/ssl \ - LOGDIR=/var/log/puppetlabs/puppetserver \ - PUPPETSERVER_HOSTNAME="" \ - DNS_ALT_NAMES="" \ - PUPPET_MASTERPORT=8140 \ - AUTOSIGN="" \ - PUPPETSERVER_MAX_ACTIVE_INSTANCES=1 \ - PUPPETSERVER_MAX_REQUESTS_PER_INSTANCE=0 \ - CA_ENABLED=true \ - CA_HOSTNAME=puppet \ - CA_MASTERPORT=8140 \ - CA_ALLOW_SUBJECT_ALT_NAMES=false \ - USE_PUPPETDB=true \ - PUPPETDB_SERVER_URLS=https://puppetdb:8081 \ - PUPPET_STORECONFIGS_BACKEND="puppetdb" \ - PUPPET_STORECONFIGS=true \ - PUPPET_REPORTS="puppetdb" - -# NOTE: this is just documentation on defaults -EXPOSE 8140 - -ENTRYPOINT ["dumb-init", "/docker-entrypoint.sh"] -CMD ["foreground"] - -ADD https://github.com/Yelp/dumb-init/releases/download/v"$DUMB_INIT_VERSION"/dumb-init_"$DUMB_INIT_VERSION"_amd64.deb / - -COPY docker/puppetserver/docker-entrypoint.sh \ - docker/puppetserver/healthcheck.sh \ - / -COPY docker/puppetserver/docker-entrypoint.d /docker-entrypoint.d -# k8s uses livenessProbe, startupProbe, readinessProbe and ignores HEALTHCHECK -HEALTHCHECK --interval=20s --timeout=15s --retries=12 --start-period=3m CMD ["/healthcheck.sh"] - -# no need to pin versions or clear apt cache as its still being used -# hadolint ignore=DL3008,DL3009 -RUN chmod +x /docker-entrypoint.sh /healthcheck.sh /docker-entrypoint.d/*.sh && \ - apt-get update && \ - apt-get install -y --no-install-recommends $PACKAGES && \ - dpkg -i dumb-init_"$DUMB_INIT_VERSION"_amd64.deb && \ - rm dumb-init_"$DUMB_INIT_VERSION"_amd64.deb - -###################################################### -# edge (build from source) -###################################################### - -FROM ubuntu:18.04 as build - -ENV LANG="en_US.UTF-8" - -ADD https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein /usr/local/bin/lein - -# hadolint ignore=DL3008,DL3028 -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - language-pack-en \ - openjdk-8-jdk-headless \ - curl \ - git \ - ruby \ - ruby-dev \ - g++ \ - make && \ - git config --global user.name "Puppet Release Team" && \ - git config --global user.email "release@puppet.com" && \ - chmod 0755 /usr/local/bin/lein && \ - /usr/local/bin/lein && \ - gem install --no-doc bundler fpm - -COPY . /puppetserver -WORKDIR /puppetserver - -# Fixes a linux 5.6 - 5.10 kernel bug around copy_file_range syscall -# https://github.com/docker/for-linux/issues/1015 -ENV RUBYOPT=-r/puppetserver/docker/ruby-docker-copy-patch - -RUN lein clean && \ - lein install && \ - EZBAKE_ALLOW_UNREPRODUCIBLE_BUILDS=true EZBAKE_NODEPLOY=true COW=base-bionic-amd64.cow MOCK='' GEM_SOURCE=https://rubygems.org lein with-profile ezbake ezbake local-build && \ - mv /puppetserver/output/deb/bionic/*/*.deb /puppetserver.deb - -FROM base as edge - -COPY --from=build /puppetserver.deb /puppetserver.deb - -ARG UBUNTU_CODENAME -ARG install_path=/puppetserver.deb -ARG deb_uri=http://nightlies.puppet.com/apt/puppet7-nightly-release-$UBUNTU_CODENAME.deb - -###################################################### -# release (build from packages) -###################################################### - -FROM base as release - -ARG version -ARG UBUNTU_CODENAME -ARG install_path=puppetserver="$version"-1"$UBUNTU_CODENAME" -ARG deb_uri=https://apt.puppetlabs.com/puppet7-release-$UBUNTU_CODENAME.deb - -###################################################### -# final image -###################################################### - -# dynamically selects "edge" or "release" alias based on ARG -# hadolint ignore=DL3006 -FROM ${build_type} as final - -ARG build_type -ARG vcs_ref -ARG version -ARG build_date -ARG install_path -ARG deb_uri -# used by entrypoint to submit metrics to Google Analytics; -# published images should use "production" for this build_arg -ARG pupperware_analytics_stream="dev" - -# hadolint ignore=DL3020 -ADD $deb_uri /puppet.deb - -# hadolint ignore=DL3008,DL3028 -RUN dpkg -i /puppet.deb && \ - rm /puppet.deb && \ - apt-get update && \ - apt-get install --no-install-recommends -y $install_path puppetdb-termini && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* && \ - gem install --no-doc r10k && \ - puppet config set autosign true --section master && \ - cp -pr /etc/puppetlabs/puppet /var/tmp && \ - cp -pr /opt/puppetlabs/server/data/puppetserver /var/tmp && \ - rm -rf /var/tmp/puppet/ssl - -COPY docker/puppetserver/puppetserver /etc/default/puppetserver -COPY docker/puppetserver/logback.xml \ - docker/puppetserver/request-logging.xml \ - /etc/puppetlabs/puppetserver/ -COPY docker/puppetserver/puppetserver.conf /etc/puppetlabs/puppetserver/conf.d/ -COPY docker/puppetserver/puppetdb.conf /var/tmp/puppet/ - -# dynamic LABELs and ENV vars placed lower for the sake of Docker layer caching -# these are specific to analytics -ENV PUPPERWARE_ANALYTICS_STREAM="$pupperware_analytics_stream" \ - PUPPET_SERVER_VERSION="$version" - -LABEL org.label-schema.name="Puppet Server ($build_type)" \ - org.label-schema.version="$version" \ - org.label-schema.vcs-ref="$vcs_ref" \ - org.label-schema.build-date="$build_date" - -COPY docker/puppetserver/Dockerfile / diff -Nru puppetserver-7.9.5/docker/puppetserver/README.md puppetserver-8.4.0/docker/puppetserver/README.md --- puppetserver-7.9.5/docker/puppetserver/README.md 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -# [puppetlabs/puppetserver](https://github.com/puppetlabs/puppetserver) - -The Dockerfile for this image is available in the Puppetserver repository -[here][1]. - -You can run a copy of Puppet Server with the following Docker command: - - docker run --name puppet --hostname puppet puppet/puppetserver - -Although it is not strictly necessary to name the container `puppet`, this is -useful when working with the other Puppet images, as they will look for a master -on that hostname by default. - -If you would like to start the Puppet Server with your own Puppet code, you can -mount your own directory at `/etc/puppetlabs/code`: - - docker run --name puppet --hostname puppet -v ./code:/etc/puppetlabs/code/ puppet/puppetserver - -You can find out more about Puppet Server in the [official documentation][2]. - -See the [pupperware repository][3] for running a full Puppet stack using Docker -Compose. - -## Configuration - -The following environment variables are supported: - -| Name | Usage / Default | -|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **PUPPETSERVER_HOSTNAME** | The DNS name used on the masters SSL certificate - sets the `certname` and `server` in puppet.conf

Defaults to unset. | -| **DNS_ALT_NAMES** | Additional DNS names to add to the masters SSL certificate
**Note** only effective on initial run when certificates are generated | -| **PUPPET_MASTERPORT** | The port of the puppet master

`8140` | -| **AUTOSIGN** | Whether or not to enable autosigning on the puppetserver instance. Valid values are `true`, `false`, and `/path/to/autosign.conf`.

Defaults to `true`. | -| **CA_ENABLED** | Whether or not this puppetserver instance has a running CA (Certificate Authority)

`true` | -| **CA_HOSTNAME** | The DNS hostname for the puppetserver running the CA. Does nothing unless `CA_ENABLED=false`

`puppet` | -| **CA_MASTERPORT** | The listening port of the CA. Does nothing unless `CA_ENABLED=false`

`8140` | -| **CA_ALLOW_SUBJECT_ALT_NAMES** | Whether or not SSL certificates containing Subject Alternative Names should be signed by the CA. Does nothing unless `CA_ENABLED=true`.

`false` | -| **PUPPET_REPORTS** | Sets `reports` in puppet.conf

`puppetdb` | -| **PUPPET_STORECONFIGS** | Sets `storeconfigs` in puppet.conf

`true` | -| **PUPPET_STORECONFIGS_BACKEND** | Sets `storeconfigs_backend` in puppet.conf

`puppetdb` | -| **PUPPETDB_SERVER_URLS** | The `server_urls` to set in `/etc/puppetlabs/puppet/puppetdb.conf`

`https://puppetdb:8081` | -| **USE_PUPPETDB** | Whether to connect to puppetdb
Sets `PUPPET_REPORTS` to `log` and `PUPPET_STORECONFIGS` to `false` if those unset

`true` | -| **PUPPETSERVER_MAX_ACTIVE_INSTANCES** | The maximum number of JRuby instances allowed

`1` | -| **PUPPETSERVER_MAX_REQUESTS_PER_INSTANCE** | The maximum HTTP requests a JRuby instance will handle in its lifetime (disable instance flushing)

`0` | -| **PUPPETSERVER_JAVA_ARGS** | Arguments passed directly to the JVM when starting the service

`-Xms512m -Xmx512m` | -| **PUPPERWARE_ANALYTICS_ENABLED** | Set to `true` to enable Google Analytics

`false` | - -## Initialization Scripts - -If you would like to do additional initialization, add a directory called `/docker-custom-entrypoint.d/` and fill it with `.sh` scripts. -These scripts will be executed at the end of the entrypoint script, before the service is ran. - -## Persistance - -If you plan to use the in-server CA, restarting the container can cause the server's keys and certificates to change, causing agents and the server to stop trusting each other. To prevent this, you can persist the default cadir, `/etc/puppetlabs/puppetserver/ca`. For example, `docker run -v $PWD/ca-ssl:/etc/puppetlabs/puppetserver/ca puppetlabs/puppetserver:latest`. - -## Analytics Data Collection - -The puppetserver container collects usage data. This is disabled by default. You can enable it by passing `--env PUPPERWARE_ANALYTICS_ENABLED=true` -to your `docker run` command. - -### What data is collected? -* Version of the puppetserver container. -* Anonymized IP address is used by Google Analytics for Geolocation data, but the IP address is not collected. - -### Why does the puppetserver container collect data? - -We collect data to help us understand how the containers are used and make decisions about upcoming changes. - -### How can I opt out of puppetserver container data collection? - -This is disabled by default. - - -[1]: https://github.com/puppetlabs/puppetserver/blob/master/docker/puppetserver/Dockerfile -[2]: https://puppet.com/docs/puppetserver/latest/services_master_puppetserver.html -[3]: https://github.com/puppetlabs/pupperware diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/10-analytics.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/10-analytics.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/10-analytics.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/10-analytics.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -#!/bin/sh - -if [ "${PUPPERWARE_ANALYTICS_ENABLED}" = "false" ]; then - echo "($0) Pupperware analytics disabled; skipping metric submission" - exit 0 -fi - -# See: https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters -# Tracking ID -tid=$PUPPERWARE_ANALYTICS_TRACKING_ID -# Application Name -an=$PUPPERWARE_ANALYTICS_APP_NAME -# Application Version -av=$PUPPET_SERVER_VERSION -# Anonymous Client ID -_file=/var/tmp/pwclientid -cid=$(cat $_file 2>/dev/null || (cat /proc/sys/kernel/random/uuid | tee $_file)) -# Event Category -ec=${PUPPERWARE_ANALYTICS_STREAM:-dev} -# Event Action -ea=start -# Anonymize ip -aip=1 - -_params="v=1&t=event&tid=${tid}&an=${an}&av=${av}&cid=${cid}&ec=${ec}&ea=${ea}&aip=${aip}" -_url="http://www.google-analytics.com/collect?${_params}" - -echo "($0) Sending metrics ${_url}" -curl --fail --silent --show-error --output /dev/null \ - -X POST -H "Content-Length: 0" $_url diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/20-use-templates-initially.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/20-use-templates-initially.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/20-use-templates-initially.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/20-use-templates-initially.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -#! /bin/bash - -# During build, pristine config files get copied to this directory. If -# they are not in the current container, use these templates as the -# default -TEMPLATES=/var/tmp/puppet - -cd /etc/puppetlabs/puppet -for f in auth.conf hiera.yaml puppet.conf puppetdb.conf -do - test -f "$TEMPLATES/$f" && cp -np "$TEMPLATES/$f" . -done -cd / - -if [ -d /var/tmp/puppetserver/vendored-jruby-gems ]; then - echo "Upgrading /opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems" - # clean up existing vendored gems - rm -rf /opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems - cp -R /var/tmp/puppetserver/vendored-jruby-gems /opt/puppetlabs/server/data/puppetserver/ - # remove the tmp dir so we only run this on first runs of new containers - rm -rf /var/tmp/puppetserver/vendored-jruby-gems -fi diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/30-set-permissions.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/30-set-permissions.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/30-set-permissions.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/30-set-permissions.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -#! /bin/bash - -chown -R puppet:puppet /etc/puppetlabs/puppet/ -chown -R puppet:puppet /opt/puppetlabs/server/data/puppetserver/ -chown -R puppet:puppet /etc/puppetlabs/puppetserver/ -chown -R puppet:puppet /var/log/puppetlabs/puppetserver/ diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/40-update-puppetdb-conf.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/40-update-puppetdb-conf.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/40-update-puppetdb-conf.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/40-update-puppetdb-conf.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -#!/bin/bash - -if test -n "${PUPPETDB_SERVER_URLS}" ; then - sed -i "s@^server_urls.*@server_urls = ${PUPPETDB_SERVER_URLS}@" /etc/puppetlabs/puppet/puppetdb.conf -fi diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/50-set-certname.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/50-set-certname.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/50-set-certname.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/50-set-certname.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -#!/bin/bash - -if test -n "${PUPPETSERVER_HOSTNAME}"; then - /opt/puppetlabs/bin/puppet config set certname "$PUPPETSERVER_HOSTNAME" - /opt/puppetlabs/bin/puppet config set server "$PUPPETSERVER_HOSTNAME" -fi diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/55-set-masterport.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/55-set-masterport.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/55-set-masterport.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/55-set-masterport.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -#!/bin/bash - -hocon() { - /opt/puppetlabs/puppet/lib/ruby/vendor_gems/bin/hocon "$@" -} - -if test -n "$PUPPET_MASTERPORT"; then - cd /etc/puppetlabs/puppetserver/conf.d/ - hocon -f webserver.conf set webserver.ssl-port $PUPPET_MASTERPORT - cd / -fi diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/60-setup-autosign.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/60-setup-autosign.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/60-setup-autosign.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/60-setup-autosign.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -#!/bin/bash - -# Configure puppet to use a certificate autosign script (if it exists) -# AUTOSIGN=true|false|path_to_autosign.conf -if test -n "${AUTOSIGN}" ; then - puppet config set autosign "$AUTOSIGN" --section master -fi diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/70-set-dns-alt-names.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/70-set-dns-alt-names.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/70-set-dns-alt-names.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/70-set-dns-alt-names.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -#!/bin/bash - -# Allow setting dns_alt_names for the compilers certificate. This -# setting will only have an effect when the container is started without -# an existing certificate on the /etc/puppetlabs/puppet volume -if [ -n "${DNS_ALT_NAMES}" ] && [ "${CA_ENABLED}" != "true" ]; then - certname=$(puppet config print certname) - if test ! -f "${SSLDIR}/certs/$certname.pem" ; then - puppet config set dns_alt_names "${DNS_ALT_NAMES}" --section agent - else - actual=$(puppet config print dns_alt_names --section "${config_section}") - if test "${DNS_ALT_NAMES}" != "${actual}" ; then - echo "Warning: DNS_ALT_NAMES has been changed from the value in puppet.conf" - echo " Remove/revoke the old certificate for this to become effective" - fi - fi -fi diff -Nru puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/80-ca.sh puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/80-ca.sh --- puppetserver-7.9.5/docker/puppetserver/docker-entrypoint.d/80-ca.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/docker-entrypoint.d/80-ca.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -#!/bin/bash - -ca_running() { - status=$(curl --silent --fail --insecure "https://${CA_HOSTNAME}:${CA_MASTERPORT}/status/v1/simple") - test "$status" = "running" -} - -hocon() { - /opt/puppetlabs/puppet/lib/ruby/vendor_gems/bin/hocon "$@" -} - -if [[ "$CA_ENABLED" != "true" ]]; then - # we are just an ordinary compiler - echo "turning off CA" - cat > /etc/puppetlabs/puppetserver/services.d/ca.cfg < - - - %d %-5p [%c{2}] %m%n - - - - - - - - - -
diff -Nru puppetserver-7.9.5/docker/puppetserver/puppetdb.conf puppetserver-8.4.0/docker/puppetserver/puppetdb.conf --- puppetserver-7.9.5/docker/puppetserver/puppetdb.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/puppetdb.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -[main] -server_urls = https://puppetdb:8081 -soft_write_failure = true diff -Nru puppetserver-7.9.5/docker/puppetserver/puppetserver puppetserver-8.4.0/docker/puppetserver/puppetserver --- puppetserver-7.9.5/docker/puppetserver/puppetserver 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/puppetserver 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -########################################### -# Init settings for puppetserver -########################################### - -# Location of your Java binary (version 7 or higher) -JAVA_BIN="/usr/bin/java" - -# Modify this if you'd like to change the memory allocation, enable JMX, etc -JAVA_ARGS=$PUPPETSERVER_JAVA_ARGS - -# These normally shouldn't need to be edited if using OS packages -USER="puppet" -GROUP="puppet" -INSTALL_DIR="/opt/puppetlabs/server/apps/puppetserver" -CONFIG="/etc/puppetlabs/puppetserver/conf.d" -BOOTSTRAP_CONFIG="/etc/puppetlabs/puppetserver/services.d/,/opt/puppetlabs/server/apps/puppetserver/config/services.d/" -SERVICE_STOP_RETRIES=60 diff -Nru puppetserver-7.9.5/docker/puppetserver/puppetserver.conf puppetserver-8.4.0/docker/puppetserver/puppetserver.conf --- puppetserver-7.9.5/docker/puppetserver/puppetserver.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/puppetserver.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -# configuration for the JRuby interpreters -jruby-puppet: { - # Where the puppet-agent dependency places puppet, facter, etc... - # Puppet server expects to load Puppet from this location - ruby-load-path: [/opt/puppetlabs/puppet/lib/ruby/vendor_ruby] - - # This setting determines where JRuby will install gems. It is used for loading gems, - # and also by the `puppetserver gem` command line tool. - gem-home: /opt/puppetlabs/server/data/puppetserver/jruby-gems - - # This setting defines the complete "GEM_PATH" for jruby. If set, it should include - # the gem-home directory as well as any other directories that gems can be loaded - # from (including the vendored gems directory for gems that ship with puppetserver) - gem-path: [${jruby-puppet.gem-home}, "/opt/puppetlabs/server/data/puppetserver/vendored-jruby-gems", "/opt/puppetlabs/puppet/lib/ruby/vendor_gems"] - - # PLEASE NOTE: Use caution when modifying the below settings. Modifying - # these settings will change the value of the corresponding Puppet settings - # for Puppet Server, but not for the Puppet CLI tools. This likely will not - # be a problem with master-var-dir, master-run-dir, or master-log-dir unless - # some critical setting in puppet.conf is interpolating the value of one - # of the corresponding settings, but it is important that any changes made to - # master-conf-dir and master-code-dir are also made to the corresponding Puppet - # settings when running the Puppet CLI tools. See - # https://docs.puppetlabs.com/puppetserver/latest/puppet_conf_setting_diffs.html#overriding-puppet-settings-in-puppet-server - # for more information. - - # (optional) path to puppet conf dir; if not specified, will use - # /etc/puppetlabs/puppet - master-conf-dir: /etc/puppetlabs/puppet - - # (optional) path to puppet code dir; if not specified, will use - # /etc/puppetlabs/code - master-code-dir: /etc/puppetlabs/code - - # (optional) path to puppet var dir; if not specified, will use - # /opt/puppetlabs/server/data/puppetserver - master-var-dir: /opt/puppetlabs/server/data/puppetserver - - # (optional) path to puppet run dir; if not specified, will use - # /var/run/puppetlabs/puppetserver - master-run-dir: /var/run/puppetlabs/puppetserver - - # (optional) path to puppet log dir; if not specified, will use - # /var/log/puppetlabs/puppetserver - master-log-dir: ${LOGDIR} - - # (optional) maximum number of JRuby instances to allow - max-active-instances: ${PUPPETSERVER_MAX_ACTIVE_INSTANCES} - - # (optional) number of HTTP requests a given JRuby instance will handle in its lifetime - max-requests-per-instance: ${PUPPETSERVER_MAX_REQUESTS_PER_INSTANCE} - - # (optional) Authorize access to Puppet master endpoints via rules - # specified in the legacy Puppet auth.conf file (if true) or via rules - # specified in the Puppet Server HOCON-formatted auth.conf (if false or not - # specified). - #use-legacy-auth-conf: true -} - -# settings related to HTTPS client requests made by Puppet Server -http-client: { - # A list of acceptable protocols for making HTTPS requests - #ssl-protocols: [TLSv1.3, TLSv1.2] - - # A list of acceptable cipher suites for making HTTPS requests - #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] - - # Whether to enable http-client metrics; defaults to 'true'. - #metrics-enabled: true -} - -# Provide a reasonable default here. Use case is mounting a valid projects volume to have PS serve files from there even if -# CM did not actually put them there. -bolt: { - projects-dir: "/etc/puppetlabs/code/projects" -} - -# settings related to profiling the puppet Ruby code -profiler: { - # enable or disable profiling for the Ruby code; defaults to 'true'. - #enabled: true -} diff -Nru puppetserver-7.9.5/docker/puppetserver/request-logging.xml puppetserver-8.4.0/docker/puppetserver/request-logging.xml --- puppetserver-7.9.5/docker/puppetserver/request-logging.xml 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/puppetserver/request-logging.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - - %h %l %u %user %date "%r" %s %b %h %a %localPort %D - - - - - diff -Nru puppetserver-7.9.5/docker/ruby-docker-copy-patch.rb puppetserver-8.4.0/docker/ruby-docker-copy-patch.rb --- puppetserver-7.9.5/docker/ruby-docker-copy-patch.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/ruby-docker-copy-patch.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -require "fileutils" - -# Fixes a linux 5.6 - 5.10 kernel bug around copy_file_range syscall -# https://github.com/docker/for-linux/issues/1015 - -module FileUtils - class Entry_ - def copy_file(dest) - File.open(path) do |s| - File.open(dest, 'wb', s.stat.mode) do |d| - s.chmod s.lstat.mode - IO.copy_stream(s, d) - d.chmod(d.lstat.mode) - end - end - end - end -end diff -Nru puppetserver-7.9.5/docker/spec/puppetserver_spec.rb puppetserver-8.4.0/docker/spec/puppetserver_spec.rb --- puppetserver-7.9.5/docker/spec/puppetserver_spec.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/docker/spec/puppetserver_spec.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -require 'rspec/core' -require 'pupperware/spec_helper' -include Pupperware::SpecHelpers - -RSpec.configure do |c| - c.before(:suite) do - require_test_image() - teardown_cluster() - run_command('docker pull puppet/puppet-agent-ubuntu:latest') - docker_compose_up() - end - - c.after(:suite) do - emit_logs - teardown_cluster() - end -end - -describe 'puppetserver container' do - it 'should be able to run a puppet agent against the puppetserver' do - expect(run_agent('puppet-agent', 'puppetserver_test', masterport: '8141')).to eq(0) - end - - it 'should be able to run an agent against the compile master' do - expect(run_agent('compiler-agent', 'puppetserver_test', server: get_container_hostname(get_service_container('compiler')), ca: get_container_hostname(get_service_container('puppet')), ca_port: '8141')).to eq(0) - end - - it 'should have r10k available' do - result = docker_compose('exec -T puppet r10k --help') - expect(result[:status].exitstatus).to eq(0) - end -end diff -Nru puppetserver-7.9.5/ext/build_defaults.yaml puppetserver-8.4.0/ext/build_defaults.yaml --- puppetserver-7.9.5/ext/build_defaults.yaml 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ext/build_defaults.yaml 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ --- project: 'puppetserver' -repo_name: 'puppet7' -nonfinal_repo_name: 'puppet7-nightly' +repo_name: 'puppet8' +nonfinal_repo_name: 'puppet8-nightly' diff -Nru puppetserver-7.9.5/ext/project_data.yaml puppetserver-8.4.0/ext/project_data.yaml --- puppetserver-7.9.5/ext/project_data.yaml 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ext/project_data.yaml 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ --- project: 'puppetserver' -repo_name: 'puppet7' -nonfinal_repo_name: 'puppet7-nightly' +repo_name: 'puppet8' +nonfinal_repo_name: 'puppet8-nightly' diff -Nru puppetserver-7.9.5/ext/thread_test/bootstrap.cfg puppetserver-8.4.0/ext/thread_test/bootstrap.cfg --- puppetserver-7.9.5/ext/thread_test/bootstrap.cfg 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ext/thread_test/bootstrap.cfg 2024-01-15 23:29:55.000000000 +0000 @@ -2,7 +2,7 @@ puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service -puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service +puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service puppetlabs.services.master.master-service/master-service diff -Nru puppetserver-7.9.5/ext/travisci/install-java.sh puppetserver-8.4.0/ext/travisci/install-java.sh --- puppetserver-7.9.5/ext/travisci/install-java.sh 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ext/travisci/install-java.sh 2024-01-15 23:29:55.000000000 +0000 @@ -2,10 +2,11 @@ set -e -echo "Installing Java $JAVA_VERSION" + +echo "Installing Java $JAVA_VERSION on arch $TRAVIS_CPU_ARCH" sudo rm -rf /usr/local/lib/jvm/ sudo rm -rf /usr/lib/jvm/openjdk-$JAVA_VERSION sudo apt-get update sudo apt-get install -y openjdk-$JAVA_VERSION-jdk-headless -export JAVA_HOME=/usr/lib/jvm/java-$JAVA_VERSION-openjdk-amd64/ +export JAVA_HOME=/usr/lib/jvm/java-$JAVA_VERSION-openjdk-$TRAVIS_CPU_ARCH/ diff -Nru puppetserver-7.9.5/ezbake/config/conf.d/auth.conf puppetserver-8.4.0/ezbake/config/conf.d/auth.conf --- puppetserver-7.9.5/ezbake/config/conf.d/auth.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ezbake/config/conf.d/auth.conf 2024-01-15 23:29:55.000000000 +0000 @@ -57,6 +57,18 @@ name: "puppetlabs csr" }, { + # Allow nodes to renew their certificate + match-request: { + path: "/puppet-ca/v1/certificate_renewal" + type: path + method: post + } + # this endpoint should never be unauthenticated, as it requires the cert to be provided. + allow: "*" + sort-order: 500 + name: "puppetlabs certificate renewal" + }, + { # Allow the CA CLI to access the certificate_status endpoint match-request: { path: "/puppet-ca/v1/certificate_status" @@ -127,6 +139,36 @@ name: "puppetlabs cert clean" }, { + # Allow the CA CLI to access the certificate sign endpoint + match-request: { + path: "/puppet-ca/v1/sign" + type: path + method: post + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert sign" + }, + { + # Allow the CA CLI to access the certificate sign all endpoint + match-request: { + path: "/puppet-ca/v1/sign/all" + type: path + method: post + } + allow: { + extensions: { + pp_cli_auth: "true" + } + } + sort-order: 500 + name: "puppetlabs cert sign all" + }, + { # Allow unauthenticated access to the status service endpoint match-request: { path: "/status/v1/services" diff -Nru puppetserver-7.9.5/ezbake/config/conf.d/ca.conf puppetserver-8.4.0/ezbake/config/conf.d/ca.conf --- puppetserver-7.9.5/ezbake/config/conf.d/ca.conf 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ezbake/config/conf.d/ca.conf 2024-01-15 23:29:55.000000000 +0000 @@ -7,4 +7,10 @@ # enable the separate CRL for Puppet infrastructure nodes # enable-infra-crl: false + # Disable auto renewal of certs by default. + allow-auto-renewal: false + # This value determines the lifetime of the cert if auto-renewal is enabled + auto-renewal-cert-ttl: "60d" + # Default cert expiration time. If the value is set here, it will take precedence over ca-ttl setting in puppet.conf + #ca-ttl: "60d" } diff -Nru puppetserver-7.9.5/ezbake/system-config/services.d/bootstrap.cfg puppetserver-8.4.0/ezbake/system-config/services.d/bootstrap.cfg --- puppetserver-7.9.5/ezbake/system-config/services.d/bootstrap.cfg 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/ezbake/system-config/services.d/bootstrap.cfg 2024-01-15 23:29:55.000000000 +0000 @@ -2,7 +2,7 @@ puppetlabs.services.jruby.jruby-puppet-service/jruby-puppet-pooled-service puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service/jruby-pool-manager-service puppetlabs.services.puppet-profiler.puppet-profiler-service/puppet-profiler-service -puppetlabs.trapperkeeper.services.webserver.jetty9-service/jetty9-service +puppetlabs.trapperkeeper.services.webserver.jetty10-service/jetty10-service puppetlabs.trapperkeeper.services.webrouting.webrouting-service/webrouting-service puppetlabs.services.config.puppet-server-config-service/puppet-server-config-service puppetlabs.services.master.master-service/master-service diff -Nru puppetserver-7.9.5/locales/puppetserver.pot puppetserver-8.4.0/locales/puppetserver.pot --- puppetserver-7.9.5/locales/puppetserver.pot 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/locales/puppetserver.pot 2024-01-15 23:29:55.000000000 +0000 @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: puppetlabs.puppetserver \n" -"X-Git-Ref: a3f5c3fd14f536f5e8089c2682c432b28069c704\n" +"X-Git-Ref: f3edc7e8a031be290af5a8ac2e04a2357968bfe0\n" "Report-Msgid-Bugs-To: docs@puppet.com\n" "POT-Creation-Date: \n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" @@ -20,6 +20,12 @@ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "" +"Detected ca-ttl setting in CA config which will take precedence over puppet." +"conf setting" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Cannot initialize {0} with partial state; need all files or none." msgstr "" @@ -32,6 +38,62 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Appending serial number {0} for {1} " +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Begin append to serial file." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Inventory serial file not found. Assume empty." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Error while appending to serial file." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Finish append to serial file. " +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Begin append to inventory file." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Inventory file not found. Assume empty." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Error while appending to inventory file." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Finish append to inventory file. " +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Append \"{1}\" to inventory file {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Looking for \"{0}\" in inventory file {1}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Extracting expired serials from inventory file {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Looking for serial numbers for \"{0}\" in inventory file {1}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Unable to find inventory file {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Config setting ca_ttl must have a value below {0}" msgstr "" @@ -65,14 +127,31 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Failed to find certificate for Puppet Infrastructure Node: {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Initializing SSL for the CA; settings:" msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Checking \"{0}\" for validity" +msgstr "" + +#. see https://github.com/puppetlabs/clj-i18n/blob/main/README.md#single-quotes-in-messages for reasoning with double quote +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Rejecting subject \"{0}\" because it doesn''t match hostname \"{1}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Instance name \"{0}\" does not match requested key \"{1}\"" msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Rejecting subject \"{0}\" because all characters must be lowercase" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Certificate names must be lower case." msgstr "" @@ -81,10 +160,22 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Rejecting subject \"{0}\" as it ends with an invalid character" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Subject hostname format is invalid" msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Rejecting subject \"{0}\" as it starts with an invalid character" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Rejecting subject \"{0}\" because it contains invalid characters" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Found extensions that are not permitted: {0}" msgstr "" @@ -178,7 +269,37 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "Signed certificate request for {0}" +msgid "Attempting to use {0} as CSR directory, but it is not a directory." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Autosign executable failed. Result: {0} " +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Entity {1} {2} 1 certificate: {3}." +msgid_plural "Entity {1} {2} {0} certificates: {3}." +msgstr[0] "" +msgstr[1] "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Found auto-renew-attribute {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Deleted certificate request for {0} at {1}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Path {0} exists but could not be deleted" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "No certificate request for {0} at expected path {1}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Calculating validity dates for {0} from ttl of {1} " msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj @@ -224,18 +345,6 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "Deleted certificate request for {0}" -msgstr "" - -#: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "Path {0} exists but could not be deleted" -msgstr "" - -#: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "No certificate request for {0} at expected path {1}" -msgstr "" - -#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "Cannot support delta CRL." msgstr "" @@ -284,11 +393,11 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "Removed certificate request for {0} at ''{1}''" +msgid "Certificate with serial {0} is already revoked." msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "Certificate with serial {0} is already revoked." +msgid "Try to read serial from \"{0}\"" msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj @@ -296,12 +405,6 @@ msgstr "" #: src/clj/puppetlabs/puppetserver/certificate_authority.clj -msgid "Revoked 1 certificate: {1}" -msgid_plural "Revoked {0} certificates: {1}" -msgstr[0] "" -msgstr[1] "" - -#: src/clj/puppetlabs/puppetserver/certificate_authority.clj msgid "No revoke action needed. The infra certs are already in the infra CRL" msgstr "" @@ -319,6 +422,34 @@ "will not be loaded" msgstr "" +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Renewed certificate for \"{0}\" with new expiration of \"{1}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "CA CRL expiring within 30 days, updating." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "infra crl expiring within 30 days, updating." +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Calculating validity dates from ttl of {0} " +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "Failed in bulk signing for entry {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "File exists at {0}" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/certificate_authority.clj +msgid "File does not exist at {0}" +msgstr "" + #: src/clj/puppetlabs/puppetserver/cli/subcommand.clj msgid "" "Path to a configuration file or directory of configuration files. See the " @@ -335,6 +466,66 @@ "'';'', or '':''" msgstr "" +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Attempt to acquire read lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Acquired read lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Released read lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Read Lock acquisition timed out \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Failed to acquire read lock \"{0}\" within {1} seconds" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Attempt to acquire write lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Acquired write lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Released write lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Write Lock acquisition timed out \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Failed to acquire write lock \"{0}\" within {1} seconds" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Attempt to acquire lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Acquired lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Released lock \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Lock acquisition timed out \"{0}\"" +msgstr "" + +#: src/clj/puppetlabs/puppetserver/common.clj +msgid "Failed to acquire lock \"{0}\" within {1} seconds" +msgstr "" + #: src/clj/puppetlabs/puppetserver/jruby_request.clj msgid "Error {0} on SERVER at {1}: {2}" msgstr "" @@ -362,6 +553,22 @@ msgstr "" #: src/clj/puppetlabs/services/analytics/analytics_service.clj +msgid "Running dropsonde" +msgstr "" + +#: src/clj/puppetlabs/services/analytics/analytics_service.clj +msgid "dropsonde run complete" +msgstr "" + +#: src/clj/puppetlabs/services/analytics/analytics_service.clj +msgid "Failed while running dropsonde" +msgstr "" + +#: src/clj/puppetlabs/services/analytics/analytics_service.clj +msgid "Failed to check for product updates" +msgstr "" + +#: src/clj/puppetlabs/services/analytics/analytics_service.clj msgid "Not checking for updates - opt-out setting exists" msgstr "" @@ -371,6 +578,10 @@ "background" msgstr "" +#: src/clj/puppetlabs/services/analytics/analytics_service.clj +msgid "Puppet Server Update Service shutting down" +msgstr "" + #: src/clj/puppetlabs/services/analytics/dropsonde.clj msgid "Successfully submitted module metrics via Dropsonde." msgstr "" @@ -392,6 +603,47 @@ msgstr "" #: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "Error while cleaning certs" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "Unable to find infra-nodes file at {0}" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "infra-nodes-path is nil, cannot validate certificate is allowed." +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "" +"Rejecting certificate request because the {0} was specified and the " +"certificate is revoked." +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "" +"Rejecting certificate request because the {0} header was specified, but the " +"client making the request was not in the allow list." +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "" +"Request is missing a certificate for an endpoint that requires a certificate." +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "Certificate present, processing renewal request" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "Certificate present, but does not match signature" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj +msgid "No valid certificate found in renewal request" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_core.clj msgid "Cannot revoke certificate for host {0} without a signed certificate" msgstr "" @@ -442,9 +694,25 @@ msgstr "" #: src/clj/puppetlabs/services/ca/certificate_authority_service.clj +msgid "Failed to evaluate crls for expiration" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_service.clj +msgid "Reporting CA event failed with: {0}nPayload: {1}" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_service.clj msgid "CA Service adding a ring handler" msgstr "" +#: src/clj/puppetlabs/services/ca/certificate_authority_service.clj +msgid "Starting CA service" +msgstr "" + +#: src/clj/puppetlabs/services/ca/certificate_authority_service.clj +msgid "Stopping CA service" +msgstr "" + #: src/clj/puppetlabs/services/config/puppet_server_config_core.clj msgid "" "The following configuration keys conflict with the values to be read from " diff -Nru puppetserver-7.9.5/project.clj puppetserver-8.4.0/project.clj --- puppetserver-7.9.5/project.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/project.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,4 +1,4 @@ -(def ps-version "7.9.5") +(def ps-version "8.4.0") (defn deploy-info [url] @@ -27,13 +27,12 @@ :min-lein-version "2.9.1" - :parent-project {:coords [puppetlabs/clj-parent "5.2.16"] + :parent-project {:coords [puppetlabs/clj-parent "7.3.1"] :inherit [:managed-dependencies]} :dependencies [[org.clojure/clojure] [slingshot] - [clj-commons/clj-yaml] [org.yaml/snakeyaml] [commons-lang] [commons-io] @@ -45,6 +44,7 @@ [liberator] [org.apache.commons/commons-exec] [io.dropwizard.metrics/metrics-core] + [org.yaml/snakeyaml "2.0"] ;; We do not currently use this dependency directly, but ;; we have documentation that shows how users can use it to @@ -54,10 +54,10 @@ [puppetlabs/jruby-utils] [puppetlabs/clj-shell-utils] [puppetlabs/trapperkeeper] - [puppetlabs/trapperkeeper-webserver-jetty9] + [com.puppetlabs/trapperkeeper-webserver-jetty10] [puppetlabs/trapperkeeper-authorization] [puppetlabs/trapperkeeper-comidi-metrics] - [puppetlabs/trapperkeeper-metrics] + [puppetlabs/trapperkeeper-metrics "2.0.1"] [puppetlabs/trapperkeeper-scheduler] [puppetlabs/trapperkeeper-status] [puppetlabs/trapperkeeper-filesystem-watcher] @@ -67,7 +67,8 @@ [puppetlabs/dujour-version-check] [puppetlabs/http-client] [puppetlabs/comidi] - [puppetlabs/i18n]] + [puppetlabs/i18n] + [puppetlabs/rbac-client]] :main puppetlabs.trapperkeeper.main @@ -83,6 +84,7 @@ ["snapshots" "https://artifactory.delivery.puppetlabs.net/artifactory/clojure-snapshots__local/"]] :plugins [[lein-parent "0.3.7"] + [jonase/eastwood "1.2.2" :exclusions [org.clojure/clojure]] ;; We have to have this, and it needs to agree with clj-parent ;; until/unless you can have managed plugin dependencies. [puppetlabs/i18n "0.9.2" :hooks false]] @@ -92,11 +94,12 @@ :group "puppet" :numeric-uid-gid 52 :build-type "foss" + :puppet-platform-version 8 :java-args ~(str "-Xms2g -Xmx2g " "-Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger") :create-dirs ["/opt/puppetlabs/server/data/puppetserver/jars"] - :repo-target "puppet7" - :nonfinal-repo-target "puppet7-nightly" + :repo-target "puppet8" + :nonfinal-repo-target "puppet8-nightly" :bootstrap-source :services-d :logrotate-enabled false} :resources {:dir "tmp/ezbake-resources"} @@ -114,32 +117,33 @@ :profiles {:defaults {:source-paths ["dev"] :dependencies [[org.clojure/tools.namespace] - [puppetlabs/trapperkeeper-webserver-jetty9 :classifier "test"] + [com.puppetlabs/trapperkeeper-webserver-jetty10 :classifier "test"] [puppetlabs/trapperkeeper nil :classifier "test" :scope "test"] - [puppetlabs/trapperkeeper-metrics :classifier "test" :scope "test"] + [puppetlabs/trapperkeeper-metrics "2.0.1" :classifier "test" :scope "test"] [puppetlabs/kitchensink nil :classifier "test" :scope "test"] [ring-basic-authentication] [ring/ring-mock] [beckon] - [lambdaisland/uri "1.4.70"]]} - :dev [:defaults - {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]}] - :fips [:defaults - {:dependencies [[org.bouncycastle/bcpkix-fips] - [org.bouncycastle/bc-fips] - [org.bouncycastle/bctls-fips]] - :jvm-opts ~(let [version (System/getProperty "java.specification.version") - [major minor _] (clojure.string/split version #"\.") - unsupported-ex (ex-info "Unsupported major Java version." - {:major major - :minor minor})] - (condp = (java.lang.Integer/parseInt major) - 1 (if (= 8 (java.lang.Integer/parseInt minor)) - ["-Djava.security.properties==./dev-resources/java.security.jdk8-fips"] - (throw unsupported-ex)) - 11 ["-Djava.security.properties==./dev-resources/java.security.jdk11on-fips"] - 17 ["-Djava.security.properties==./dev-resources/java.security.jdk11on-fips"] - (throw unsupported-ex)))}] + [lambdaisland/uri "1.4.70"] + [puppetlabs/rbac-client :classifier "test" :scope "test"]]} + :dev-deps {:dependencies [[org.bouncycastle/bcpkix-jdk18on]]} + :dev [:defaults :dev-deps] + :fips-deps {:dependencies [[org.bouncycastle/bcpkix-fips] + [org.bouncycastle/bc-fips] + [org.bouncycastle/bctls-fips]] + :jvm-opts ~(let [version (System/getProperty "java.specification.version") + [major minor _] (clojure.string/split version #"\.") + unsupported-ex (ex-info "Unsupported major Java version." + {:major major + :minor minor})] + (condp = (java.lang.Integer/parseInt major) + 1 (if (= 8 (java.lang.Integer/parseInt minor)) + ["-Djava.security.properties==./dev-resources/java.security.jdk8-fips"] + (throw unsupported-ex)) + 11 ["-Djava.security.properties==./dev-resources/java.security.jdk11on-fips"] + 17 ["-Djava.security.properties==./dev-resources/java.security.jdk11on-fips"] + (throw unsupported-ex)))} + :fips [:defaults :fips-deps] :testutils {:source-paths ["test/unit" "test/integration"]} :test { @@ -167,17 +171,18 @@ [org.bouncycastle/bcpkix-jdk18on] [puppetlabs/jruby-utils] [puppetlabs/puppetserver ~ps-version] - [puppetlabs/trapperkeeper-webserver-jetty9]] - :plugins [[puppetlabs/lein-ezbake "2.3.2"]] + [com.puppetlabs/trapperkeeper-webserver-jetty10] + [puppetlabs/trapperkeeper-metrics "2.0.1"]] + :plugins [[puppetlabs/lein-ezbake "2.5.5"]] :name "puppetserver"} :uberjar {:dependencies [[org.bouncycastle/bcpkix-jdk18on] - [puppetlabs/trapperkeeper-webserver-jetty9]] + [com.puppetlabs/trapperkeeper-webserver-jetty10]] :aot [puppetlabs.trapperkeeper.main puppetlabs.trapperkeeper.services.status.status-service puppetlabs.trapperkeeper.services.metrics.metrics-service puppetlabs.services.protocols.jruby-puppet puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service - puppetlabs.trapperkeeper.services.webserver.jetty9-service + puppetlabs.trapperkeeper.services.webserver.jetty10-service puppetlabs.trapperkeeper.services.webrouting.webrouting-service puppetlabs.services.legacy-routes.legacy-routes-core puppetlabs.services.protocols.jruby-metrics @@ -232,6 +237,11 @@ :multithreaded (complement :single-threaded-only) :singlethreaded (complement :multithreaded-only)} + :eastwood {:exclude-linters [:unused-meta-on-macro + :reflection + [:suspicious-test :second-arg-is-not-string]] + :continue-on-exception true} + :aliases {"gem" ["trampoline" "run" "-m" "puppetlabs.puppetserver.cli.gem" "--config" "./dev/puppetserver.conf" "--"] "ruby" ["trampoline" "run" "-m" "puppetlabs.puppetserver.cli.ruby" "--config" "./dev/puppetserver.conf" "--"] "irb" ["trampoline" "run" "-m" "puppetlabs.puppetserver.cli.irb" "--config" "./dev/puppetserver.conf" "--"] diff -Nru puppetserver-7.9.5/resources/ext/build-scripts/jruby-gem-list.txt puppetserver-8.4.0/resources/ext/build-scripts/jruby-gem-list.txt --- puppetserver-7.9.5/resources/ext/build-scripts/jruby-gem-list.txt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/resources/ext/build-scripts/jruby-gem-list.txt 2024-01-15 23:29:55.000000000 +0000 @@ -4,5 +4,5 @@ locale 2.1.2 gettext 3.2.2 fast_gettext 1.1.2 -concurrent-ruby 1.1.5 +concurrent-ruby 1.2.2 deep_merge 1.0.1 diff -Nru puppetserver-7.9.5/resources/ext/build-scripts/mri-gem-list-no-dependencies.txt puppetserver-8.4.0/resources/ext/build-scripts/mri-gem-list-no-dependencies.txt --- puppetserver-7.9.5/resources/ext/build-scripts/mri-gem-list-no-dependencies.txt 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/resources/ext/build-scripts/mri-gem-list-no-dependencies.txt 2024-01-15 23:29:55.000000000 +0000 @@ -1 +1 @@ -puppetserver-ca 2.4.0 +puppetserver-ca 2.6.0 diff -Nru puppetserver-7.9.5/resources/ext/cli_defaults/cli-defaults.sh.erb puppetserver-8.4.0/resources/ext/cli_defaults/cli-defaults.sh.erb --- puppetserver-7.9.5/resources/ext/cli_defaults/cli-defaults.sh.erb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/resources/ext/cli_defaults/cli-defaults.sh.erb 2024-01-15 23:29:55.000000000 +0000 @@ -4,4 +4,20 @@ echo "Warning: the JRUBY_JAR setting is no longer needed and will be ignored." 1>&2 fi +java_version=$($JAVA_BIN -version 2>&1 | head -1 | awk -F\" '{ print $2 }') +java_major_version=$(echo $java_version | awk -F. '{ print $1 }') + +if [[ $java_major_version -eq 17 ]]; then + + echo $JAVA_ARGS | grep "add-opens" &>/dev/null + if [[ 0 -ne $? ]]; then + export JAVA_ARGS="--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED ${JAVA_ARGS}" + fi + + echo $JAVA_ARGS_CLI | grep "add-opens" &>/dev/null + if [[ 0 -ne $? ]]; then + export JAVA_ARGS_CLI="--add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED ${JAVA_ARGS_CLI}" + fi +fi + CLASSPATH="${CLASSPATH}:/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter.jar:/opt/puppetlabs/server/data/<%= EZBake::Config[:real_name] %>/jars/*" diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/CHANGELOG.md puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/CHANGELOG.md --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/CHANGELOG.md 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/CHANGELOG.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,478 +0,0 @@ -## Current - -## Release v1.1.5, edge v0.5.0 (10 mar 2019) - -concurrent-ruby: - -* fix potential leak of context on JRuby and Java 7 - -concurrent-ruby-edge: - -* Add finalized Concurrent::Cancellation -* Add finalized Concurrent::Throttle -* Add finalized Concurrent::Promises::Channel -* Add new Concurrent::ErlangActor - -## Release v1.1.4 (14 Dec 2018) - -* (#780) Remove java_alias of 'submit' method of Runnable to let executor service work on java 11 -* (#776) Fix NameError on defining a struct with a name which is already taken in an ancestor - -## Release v1.1.3 (7 Nov 2018) - -* (#775) fix partial require of the gem (although not officially supported) - -## Release v1.1.2 (6 Nov 2018) - -* (#773) more defensive 1.9.3 support - -## Release v1.1.1, edge v0.4.1 (1 Nov 2018) - -* (#768) add support for 1.9.3 back - -## Release v1.1.0, edge v0.4.0 (31 OCt 2018) (yanked) - -* (#768) yanked because of issues with removed 1.9.3 support - -## Release v1.1.0.pre2, edge v0.4.0.pre2 (18 Sep 2018) - -concurrent-ruby: - -* fixed documentation and README links -* fix Set for TruffleRuby and Rubinius -* use properly supported TruffleRuby APIs - -concurrent-ruby-edge: - -* add Promises.zip_futures_over_on - -## Release v1.1.0.pre1, edge v0.4.0.pre1 (15 Aug 2018) - -concurrent-ruby: - -* requires at least Ruby 2.0 -* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/1.1.0/Concurrent/Promises.html) - are moved from `concurrent-ruby-edge` to `concurrent-ruby` -* Add support for TruffleRuby - * (#734) Fix Array/Hash/Set construction broken on TruffleRuby - * AtomicReference fixed -* CI stabilization -* remove sharp dependency edge -> core -* remove warnings -* documentation updates -* Exchanger is no longer documented as edge since it was already available in - `concurrent-ruby` -* (#644) Fix Map#each and #each_pair not returning enumerator outside of MRI -* (#659) Edge promises fail during error handling -* (#741) Raise on recursive Delay#value call -* (#727) #717 fix global IO executor on JRuby -* (#740) Drop support for CRuby 1.9, JRuby 1.7, Rubinius. -* (#737) Move AtomicMarkableReference out of Edge -* (#708) Prefer platform specific memory barriers -* (#735) Fix wrong expected exception in channel spec assertion -* (#729) Allow executor option in `Promise#then` -* (#725) fix timeout check to use timeout_interval -* (#719) update engine detection -* (#660) Add specs for Promise#zip/Promise.zip ordering -* (#654) Promise.zip execution changes -* (#666) Add thread safe set implementation -* (#651) #699 #to_s, #inspect should not output negative object IDs. -* (#685) Avoid RSpec warnings about raise_error -* (#680) Avoid RSpec monkey patching, persist spec results locally, use RSpec - v3.7.0 -* (#665) Initialize the monitor for new subarrays on Rubinius -* (#661) Fix error handling in edge promises - -concurrent-ruby-edge: - -* (#659) Edge promises fail during error handling -* Edge files clearly separated in `lib-edge` -* added ReInclude - -## Release v1.0.5, edge v0.3.1 (26 Feb 2017) - -concurrent-ruby: - -* Documentation for Event and Semaphore -* Use Unsafe#fullFence and #loadFence directly since the shortcuts were removed in JRuby -* Do not depend on org.jruby.util.unsafe.UnsafeHolder - -concurrent-ruby-edge: - -* (#620) Actors on Pool raise an error -* (#624) Delayed promises did not interact correctly with flatting - * Fix arguments yielded by callback methods -* Overridable default executor in promises factory methods -* Asking actor to terminate will always resolve to `true` - -## Release v1.0.4, edge v0.3.0 (27 Dec 2016) - -concurrent-ruby: - -* Nothing - -concurrent-ruby-edge: - -* New promises' API renamed, lots of improvements, edge bumped to 0.3.0 - * **Incompatible** with previous 0.2.3 version - * see https://github.com/ruby-concurrency/concurrent-ruby/pull/522 - -## Release v1.0.3 (17 Dec 2016) - -* Trigger execution of flattened delayed futures -* Avoid forking for processor_count if possible -* Semaphore Mutex and JRuby parity -* Adds Map#each as alias to Map#each_pair -* Fix uninitialized instance variables -* Make Fixnum, Bignum merger ready -* Allows Promise#then to receive an executor -* TimerSet now survives a fork -* Reject promise on any exception -* Allow ThreadLocalVar to be initialized with a block -* Support Alpha with `Concurrent::processor_count` -* Fixes format-security error when compiling ruby_193_compatible.h -* Concurrent::Atom#swap fixed: reraise the exceptions from block - -## Release v1.0.2 (2 May 2016) - -* Fix bug with `Concurrent::Map` MRI backend `#inspect` method -* Fix bug with `Concurrent::Map` MRI backend using `Hash#value?` -* Improved documentation and examples -* Minor updates to Edge - -## Release v1.0.1 (27 February 2016) - -* Fix "uninitialized constant Concurrent::ReentrantReadWriteLock" error. -* Better handling of `autoload` vs. `require`. -* Improved API for Edge `Future` zipping. -* Fix reference leak in Edge `Future` constructor . -* Fix bug which prevented thread pools from surviving a `fork`. -* Fix bug in which `TimerTask` did not correctly specify all its dependencies. -* Improved support for JRuby+Truffle -* Improved error messages. -* Improved documentation. -* Updated README and CONTRIBUTING. - -## Release v1.0.0 (13 November 2015) - -* Rename `attr_volatile_with_cas` to `attr_atomic` -* Add `clear_each` to `LockFreeStack` -* Update `AtomicReference` documentation -* Further updates and improvements to the synchronization layer. -* Performance and memory usage performance with `Actor` logging. -* Fixed `ThreadPoolExecutor` task count methods. -* Improved `Async` performance for both short and long-lived objects. -* Fixed bug in `LockFreeLinkedSet`. -* Fixed bug in which `Agent#await` triggered a validation failure. -* Further `Channel` updates. -* Adopted a project Code of Conduct -* Cleared interpreter warnings -* Fixed bug in `ThreadPoolExecutor` task count methods -* Fixed bug in 'LockFreeLinkedSet' -* Improved Java extension loading -* Handle Exception children in Edge::Future -* Continued improvements to channel -* Removed interpreter warnings. -* Shared constants now in `lib/concurrent/constants.rb` -* Refactored many tests. -* Improved synchronization layer/memory model documentation. -* Bug fix in Edge `Future#flat` -* Brand new `Channel` implementation in Edge gem. -* Simplification of `RubySingleThreadExecutor` -* `Async` improvements - - Each object uses its own `SingleThreadExecutor` instead of the global thread pool. - - No longers supports executor injection - - Much better documentation -* `Atom` updates - - No longer `Dereferenceable` - - Now `Observable` - - Added a `#reset` method -* Brand new `Agent` API and implementation. Now functionally equivalent to Clojure. -* Continued improvements to the synchronization layer -* Merged in the `thread_safe` gem - - `Concurrent::Array` - - `Concurrent::Hash` - - `Concurrent::Map` (formerly ThreadSafe::Cache) - - `Concurrent::Tuple` -* Minor improvements to Concurrent::Map -* Complete rewrite of `Exchanger` -* Removed all deprecated code (classes, methods, constants, etc.) -* Updated Agent, MutexAtomic, and BufferedChannel to inherit from Synchronization::Object. -* Many improved tests -* Some internal reorganization - -## Release v0.9.1 (09 August 2015) - -* Fixed a Rubiniux bug in synchronization object -* Fixed all interpreter warnings (except circular references) -* Fixed require statements when requiring `Atom` alone -* Significantly improved `ThreadLocalVar` on non-JRuby platforms -* Fixed error handling in Edge `Concurrent.zip` -* `AtomicFixnum` methods `#increment` and `#decrement` now support optional delta -* New `AtomicFixnum#update` method -* Minor optimizations in `ReadWriteLock` -* New `ReentrantReadWriteLock` class -* `ThreadLocalVar#bind` method is now public -* Refactored many tests - -## Release v0.9.0 (10 July 2015) - -* Updated `AtomicReference` - - `AtomicReference#try_update` now simply returns instead of raising exception - - `AtomicReference#try_update!` was added to raise exceptions if an update - fails. Note: this is the same behavior as the old `try_update` -* Pure Java implementations of - - `AtomicBoolean` - - `AtomicFixnum` - - `Semaphore` -* Fixed bug when pruning Ruby thread pools -* Fixed bug in time calculations within `ScheduledTask` -* Default `count` in `CountDownLatch` to 1 -* Use monotonic clock for all timers via `Concurrent.monotonic_time` - - Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` when available - - Fallback to `java.lang.System.nanoTime()` on unsupported JRuby versions - - Pure Ruby implementation for everything else - - Effects `Concurrent.timer`, `Concurrent.timeout`, `TimerSet`, `TimerTask`, and `ScheduledTask` -* Deprecated all clock-time based timer scheduling - - Only support scheduling by delay - - Effects `Concurrent.timer`, `TimerSet`, and `ScheduledTask` -* Added new `ReadWriteLock` class -* Consistent `at_exit` behavior for Java and Ruby thread pools. -* Added `at_exit` handler to Ruby thread pools (already in Java thread pools) - - Ruby handler stores the object id and retrieves from `ObjectSpace` - - JRuby disables `ObjectSpace` by default so that handler stores the object reference -* Added a `:stop_on_exit` option to thread pools to enable/disable `at_exit` handler -* Updated thread pool docs to better explain shutting down thread pools -* Simpler `:executor` option syntax for all abstractions which support this option -* Added `Executor#auto_terminate?` predicate method (for thread pools) -* Added `at_exit` handler to `TimerSet` -* Simplified auto-termination of the global executors - - Can now disable auto-termination of global executors - - Added shutdown/kill/wait_for_termination variants for global executors -* Can now disable auto-termination for *all* executors (the nuclear option) -* Simplified auto-termination of the global executors -* Deprecated terms "task pool" and "operation pool" - - New terms are "io executor" and "fast executor" - - New functions added with new names - - Deprecation warnings added to functions referencing old names -* Moved all thread pool related functions from `Concurrent::Configuration` to `Concurrent` - - Old functions still exist with deprecation warnings - - New functions have updated names as appropriate -* All high-level abstractions default to the "io executor" -* Fixed bug in `Actor` causing it to prematurely warm global thread pools on gem load - - This also fixed a `RejectedExecutionError` bug when running with minitest/autorun via JRuby -* Moved global logger up to the `Concurrent` namespace and refactored the code -* Optimized the performance of `Delay` - - Fixed a bug in which no executor option on construction caused block execution on a global thread pool -* Numerous improvements and bug fixes to `TimerSet` -* Fixed deadlock of `Future` when the handler raises Exception -* Added shared specs for more classes -* New concurrency abstractions including: - - `Atom` - - `Maybe` - - `ImmutableStruct` - - `MutableStruct` - - `SettableStruct` -* Created an Edge gem for unstable abstractions including - - `Actor` - - `Agent` - - `Channel` - - `Exchanger` - - `LazyRegister` - - **new Future Framework** - unified - implementation of Futures and Promises which combines Features of previous `Future`, - `Promise`, `IVar`, `Event`, `Probe`, `dataflow`, `Delay`, `TimerTask` into single framework. It uses extensively - new synchronization layer to make all the paths **lock-free** with exception of blocking threads on `#wait`. - It offers better performance and does not block threads when not required. -* Actor framework changes: - - fixed reset loop in Pool - - Pool can use any actor as a worker, abstract worker class is no longer needed. - - Actor events not have format `[:event_name, *payload]` instead of just the Symbol. - - Actor now uses new Future/Promise Framework instead of `IVar` for better interoperability - - Behaviour definition array was simplified to `[BehaviourClass1, [BehaviourClass2, *initialization_args]]` - - Linking behavior responds to :linked message by returning array of linked actors - - Supervised behavior is removed in favour of just Linking - - RestartingContext is supervised by default now, `supervise: true` is not required any more - - Events can be private and public, so far only difference is that Linking will - pass to linked actors only public messages. Adding private :restarting and - :resetting events which are send before the actor restarts or resets allowing - to add callbacks to cleanup current child actors. - - Print also object_id in Reference to_s - - Add AbstractContext#default_executor to be able to override executor class wide - - Add basic IO example - - Documentation somewhat improved - - All messages should have same priority. It's now possible to send `actor << job1 << job2 << :terminate!` and - be sure that both jobs are processed first. -* Refactored `Channel` to use newer synchronization objects -* Added `#reset` and `#cancel` methods to `TimerSet` -* Added `#cancel` method to `Future` and `ScheduledTask` -* Refactored `TimerSet` to use `ScheduledTask` -* Updated `Async` with a factory that initializes the object -* Deprecated `Concurrent.timer` and `Concurrent.timeout` -* Reduced max threads on pure-Ruby thread pools (abends around 14751 threads) -* Moved many private/internal classes/modules into "namespace" modules -* Removed brute-force killing of threads in tests -* Fixed a thread pool bug when the operating system cannot allocate more threads - -## Release v0.8.0 (25 January 2015) - -* C extension for MRI have been extracted into the `concurrent-ruby-ext` companion gem. - Please see the README for more detail. -* Better variable isolation in `Promise` and `Future` via an `:args` option -* Continued to update intermittently failing tests - -## Release v0.7.2 (24 January 2015) - -* New `Semaphore` class based on [java.util.concurrent.Semaphore](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html) -* New `Promise.all?` and `Promise.any?` class methods -* Renamed `:overflow_policy` on thread pools to `:fallback_policy` -* Thread pools still accept the `:overflow_policy` option but display a warning -* Thread pools now implement `fallback_policy` behavior when not running (rather than universally rejecting tasks) -* Fixed minor `set_deref_options` constructor bug in `Promise` class -* Fixed minor `require` bug in `ThreadLocalVar` class -* Fixed race condition bug in `TimerSet` class -* Fixed race condition bug in `TimerSet` class -* Fixed signal bug in `TimerSet#post` method -* Numerous non-functional updates to clear warning when running in debug mode -* Fixed more intermittently failing tests -* Tests now run on new Travis build environment -* Multiple documentation updates - -## Release v0.7.1 (4 December 2014) - -Please see the [roadmap](https://github.com/ruby-concurrency/concurrent-ruby/issues/142) for more information on the next planned release. - -* Added `flat_map` method to `Promise` -* Added `zip` method to `Promise` -* Fixed bug with logging in `Actor` -* Improvements to `Promise` tests -* Removed actor-experimental warning -* Added an `IndirectImmediateExecutor` class -* Allow disabling auto termination of global executors -* Fix thread leaking in `ThreadLocalVar` (uses `Ref` gem on non-JRuby systems) -* Fix thread leaking when pruning pure-Ruby thread pools -* Prevent `Actor` from using an `ImmediateExecutor` (causes deadlock) -* Added missing synchronizations to `TimerSet` -* Fixed bug with return value of `Concurrent::Actor::Utils::Pool#ask` -* Fixed timing bug in `TimerTask` -* Fixed bug when creating a `JavaThreadPoolExecutor` with minimum pool size of zero -* Removed confusing warning when not using native extenstions -* Improved documentation - -## Release v0.7.0 (13 August 2014) - -* Merge the [atomic](https://github.com/ruby-concurrency/atomic) gem - - Pure Ruby `MutexAtomic` atomic reference class - - Platform native atomic reference classes `CAtomic`, `JavaAtomic`, and `RbxAtomic` - - Automated [build process](https://github.com/ruby-concurrency/rake-compiler-dev-box) - - Fat binary releases for [multiple platforms](https://rubygems.org/gems/concurrent-ruby/versions) including Windows (32/64), Linux (32/64), OS X (64-bit), Solaris (64-bit), and JRuby -* C native `CAtomicBoolean` -* C native `CAtomicFixnum` -* Refactored intermittently failing tests -* Added `dataflow!` and `dataflow_with!` methods to match `Future#value!` method -* Better handling of timeout in `Agent` -* Actor Improvements - - Fine-grained implementation using chain of behaviors. Each behavior is responsible for single aspect like: `Termination`, `Pausing`, `Linking`, `Supervising`, etc. Users can create custom Actors easily based on their needs. - - Supervision was added. `RestartingContext` will pause on error waiting on its supervisor to decide what to do next ( options are `:terminate!`, `:resume!`, `:reset!`, `:restart!`). Supervising behavior also supports strategies `:one_for_one` and `:one_for_all`. - - Linking was added to be able to monitor actor's events like: `:terminated`, `:paused`, `:restarted`, etc. - - Dead letter routing added. Rejected envelopes are collected in a configurable actor (default: `Concurrent::Actor.root.ask!(:dead_letter_routing)`) - - Old `Actor` class removed and replaced by new implementation previously called `Actress`. `Actress` was kept as an alias for `Actor` to keep compatibility. - - `Utils::Broadcast` actor which allows Publish–subscribe pattern. -* More executors for managing serialized operations - - `SerializedExecution` mixin module - - `SerializedExecutionDelegator` for serializing *any* executor -* Updated `Async` with serialized execution -* Updated `ImmediateExecutor` and `PerThreadExecutor` with full executor service lifecycle -* Added a `Delay` to root `Actress` initialization -* Minor bug fixes to thread pools -* Refactored many intermittently failing specs -* Removed Java interop warning `executor.rb:148 warning: ambiguous Java methods found, using submit(java.lang.Runnable)` -* Fixed minor bug in `RubyCachedThreadPool` overflow policy -* Updated tests to use [RSpec 3.0](http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3) -* Removed deprecated `Actor` class -* Better support for Rubinius - -## Release v0.6.1 (14 June 2014) - -* Many improvements to `Concurrent::Actress` -* Bug fixes to `Concurrent::RubyThreadPoolExecutor` -* Fixed several brittle tests -* Moved documentation to http://ruby-concurrency.github.io/concurrent-ruby/frames.html - -## Release v0.6.0 (25 May 2014) - -* Added `Concurrent::Observable` to encapsulate our thread safe observer sets -* Improvements to new `Channel` -* Major improvements to `CachedThreadPool` and `FixedThreadPool` -* Added `SingleThreadExecutor` -* Added `Current::timer` function -* Added `TimerSet` executor -* Added `AtomicBoolean` -* `ScheduledTask` refactoring -* Pure Ruby and JRuby-optimized `PriorityQueue` classes -* Updated `Agent` behavior to more closely match Clojure -* Observer sets support block callbacks to the `add_observer` method -* New algorithm for thread creation in `RubyThreadPoolExecutor` -* Minor API updates to `Event` -* Rewritten `TimerTask` now an `Executor` instead of a `Runnable` -* Fixed many brittle specs -* Renamed `FixedThreadPool` and `CachedThreadPool` to `RubyFixedThreadPool` and `RubyCachedThreadPool` -* Created JRuby optimized `JavaFixedThreadPool` and `JavaCachedThreadPool` -* Consolidated fixed thread pool tests into `spec/concurrent/fixed_thread_pool_shared.rb` and `spec/concurrent/cached_thread_pool_shared.rb` -* `FixedThreadPool` now subclasses `RubyFixedThreadPool` or `JavaFixedThreadPool` as appropriate -* `CachedThreadPool` now subclasses `RubyCachedThreadPool` or `JavaCachedThreadPool` as appropriate -* New `Delay` class -* `Concurrent::processor_count` helper function -* New `Async` module -* Renamed `NullThreadPool` to `PerThreadExecutor` -* Deprecated `Channel` (we are planning a new implementation based on [Go](http://golangtutorials.blogspot.com/2011/06/channels-in-go.html)) -* Added gem-level [configuration](http://robots.thoughtbot.com/mygem-configure-block) -* Deprecated `$GLOBAL_THREAD_POOL` in lieu of gem-level configuration -* Removed support for Ruby [1.9.2](https://www.ruby-lang.org/en/news/2013/12/17/maintenance-of-1-8-7-and-1-9-2/) -* New `RubyThreadPoolExecutor` and `JavaThreadPoolExecutor` classes -* All thread pools now extend the appropriate thread pool executor classes -* All thread pools now support `:overflow_policy` (based on Java's [reject policies](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html)) -* Deprecated `UsesGlobalThreadPool` in lieu of explicit `:executor` option (dependency injection) on `Future`, `Promise`, and `Agent` -* Added `Concurrent::dataflow_with(executor, *inputs)` method to support executor dependency injection for dataflow -* Software transactional memory with `TVar` and `Concurrent::atomically` -* First implementation of [new, high-performance](https://github.com/ruby-concurrency/concurrent-ruby/pull/49) `Channel` -* `Actor` is deprecated in favor of new experimental actor implementation [#73](https://github.com/ruby-concurrency/concurrent-ruby/pull/73). To avoid namespace collision it is living in `Actress` namespace until `Actor` is removed in next release. - -## Release v0.5.0 - -This is the most significant release of this gem since its inception. This release includes many improvements and optimizations. It also includes several bug fixes. The major areas of focus for this release were: - -* Stability improvements on Ruby versions with thread-level parallelism ([JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/)) -* Creation of new low-level concurrency abstractions -* Internal refactoring to use the new low-level abstractions - -Most of these updates had no effect on the gem API. There are a few notable exceptions which were unavoidable. Please read the [release notes](API-Updates-in-v0.5.0) for more information. - -Specific changes include: - -* New class `IVar` -* New class `MVar` -* New class `ThreadLocalVar` -* New class `AtomicFixnum` -* New class method `dataflow` -* New class `Condition` -* New class `CountDownLatch` -* New class `DependencyCounter` -* New class `SafeTaskExecutor` -* New class `CopyOnNotifyObserverSet` -* New class `CopyOnWriteObserverSet` -* `Future` updated with `execute` API -* `ScheduledTask` updated with `execute` API -* New `Promise` API -* `Future` now extends `IVar` -* `Postable#post?` now returns an `IVar` -* Thread safety fixes to `Dereferenceable` -* Thread safety fixes to `Obligation` -* Thread safety fixes to `Supervisor` -* Thread safety fixes to `Event` -* Various other thread safety (race condition) fixes -* Refactored brittle tests -* Implemented pending tests -* Added JRuby and Rubinius as Travis CI build targets -* Added [CodeClimate](https://codeclimate.com/) code review -* Improved YARD documentation diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Gemfile puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Gemfile --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Gemfile 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Gemfile 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -source 'https://rubygems.org' - -require File.join(File.dirname(__FILE__), 'lib/concurrent/version') -require File.join(File.dirname(__FILE__ ), 'lib-edge/concurrent/edge/version') - -no_path = ENV['NO_PATH'] -options = no_path ? {} : { path: '.' } - -gem 'concurrent-ruby', Concurrent::VERSION, options -gem 'concurrent-ruby-edge', Concurrent::EDGE_VERSION, options -gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri) - -group :development do - gem 'rake', '~> 12.0' - gem 'rake-compiler', '~> 1.0', '>= 1.0.7' - gem 'rake-compiler-dock', '~> 0.7.0' - gem 'pry', '~> 0.11', platforms: :mri -end - -group :documentation, optional: true do - gem 'yard', '~> 0.9.0', require: false - gem 'redcarpet', '~> 3.0', platforms: :mri # understands github markdown - gem 'md-ruby-eval', '~> 0.6' -end - -group :testing do - gem 'rspec', '~> 3.7' - gem 'timecop', '~> 0.7.4' - gem 'sigdump', require: false -end - -# made opt-in since it will not install on jruby 1.7 -group :coverage, optional: !ENV['COVERAGE'] do - gem 'simplecov', '~> 0.16.0', require: false - gem 'coveralls', '~> 0.8.2', require: false -end - -group :benchmarks, optional: true do - gem 'benchmark-ips', '~> 2.7' - gem 'bench9000' -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/LICENSE.md puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/LICENSE.md --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/LICENSE.md 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/LICENSE.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -``` -Copyright (c) Jerry D'Antonio -- released under the MIT license. - -http://www.opensource.org/licenses/mit-license.php - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/README.md puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/README.md --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/README.md 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,381 +0,0 @@ -# Concurrent Ruby - -[![Gem Version](https://badge.fury.io/rb/concurrent-ruby.svg)](http://badge.fury.io/rb/concurrent-ruby) -[![Build Status](https://travis-ci.org/ruby-concurrency/concurrent-ruby.svg?branch=master)](https://travis-ci.org/ruby-concurrency/concurrent-ruby) -[![Build status](https://ci.appveyor.com/api/projects/status/iq8aboyuu3etad4w?svg=true)](https://ci.appveyor.com/project/rubyconcurrency/concurrent-ruby) -[![License](https://img.shields.io/badge/license-MIT-green.svg)](http://opensource.org/licenses/MIT) -[![Gitter chat](https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby) - -Modern concurrency tools for Ruby. Inspired by -[Erlang](http://www.erlang.org/doc/reference_manual/processes.html), -[Clojure](http://clojure.org/concurrent_programming), -[Scala](http://akka.io/), -[Haskell](http://www.haskell.org/haskellwiki/Applications_and_libraries/Concurrency_and_parallelism#Concurrent_Haskell), -[F#](http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx), -[C#](http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx), -[Java](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html), -and classic concurrency patterns. - - - -The design goals of this gem are: - -* Be an 'unopinionated' toolbox that provides useful utilities without debating which is better - or why -* Remain free of external gem dependencies -* Stay true to the spirit of the languages providing inspiration -* But implement in a way that makes sense for Ruby -* Keep the semantics as idiomatic Ruby as possible -* Support features that make sense in Ruby -* Exclude features that don't make sense in Ruby -* Be small, lean, and loosely coupled -* Thread-safety -* Backward compatibility - -## Contributing - -**This gem depends on -[contributions](https://github.com/ruby-concurrency/concurrent-ruby/graphs/contributors) and we -appreciate your help. Would you like to contribute? Great! Have a look at -[issues with `looking-for-contributor` label](https://github.com/ruby-concurrency/concurrent-ruby/issues?q=is%3Aissue+is%3Aopen+label%3Alooking-for-contributor).** And if you pick something up let us know on the issue. - -## Thread Safety - -*Concurrent Ruby makes one of the strongest thread safety guarantees of any Ruby concurrency -library, providing consistent behavior and guarantees on all four of the main Ruby interpreters -(MRI/CRuby, JRuby, Rubinius, TruffleRuby).* - -Every abstraction in this library is thread safe. Specific thread safety guarantees are documented -with each abstraction. - -It is critical to remember, however, that Ruby is a language of mutable references. *No* -concurrency library for Ruby can ever prevent the user from making thread safety mistakes (such as -sharing a mutable object between threads and modifying it on both threads) or from creating -deadlocks through incorrect use of locks. All the library can do is provide safe abstractions which -encourage safe practices. Concurrent Ruby provides more safe concurrency abstractions than any -other Ruby library, many of which support the mantra of -["Do not communicate by sharing memory; instead, share memory by communicating"](https://blog.golang.org/share-memory-by-communicating). -Concurrent Ruby is also the only Ruby library which provides a full suite of thread safe and -immutable variable types and data structures. - -We've also initiated discussion to document [memory model](docs-source/synchronization.md) of Ruby which -would provide consistent behaviour and guarantees on all four of the main Ruby interpreters -(MRI/CRuby, JRuby, Rubinius, TruffleRuby). - -## Features & Documentation - -**The primary site for documentation is the automatically generated -[API documentation](http://ruby-concurrency.github.io/concurrent-ruby/index.html) which is up to -date with latest release.** This readme matches the master so may contain new stuff not yet -released. - -We also have a [IRC (gitter)](https://gitter.im/ruby-concurrency/concurrent-ruby). - -### Versioning - -* `concurrent-ruby` uses [Semantic Versioning](http://semver.org/) -* `concurrent-ruby-ext` has always same version as `concurrent-ruby` -* `concurrent-ruby-edge` will always be 0.y.z therefore following - [point 4](http://semver.org/#spec-item-4) applies *"Major version zero - (0.y.z) is for initial development. Anything may change at any time. The - public API should not be considered stable."* However we additionally use - following rules: - * Minor version increment means incompatible changes were made - * Patch version increment means only compatible changes were made - - -#### General-purpose Concurrency Abstractions - -* [Async](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Async.html): - A mixin module that provides simple asynchronous behavior to a class. Loosely based on Erlang's - [gen_server](http://www.erlang.org/doc/man/gen_server.html). -* [ScheduledTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ScheduledTask.html): - Like a Future scheduled for a specific future time. -* [TimerTask](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TimerTask.html): - A Thread that periodically wakes up to perform work at regular intervals. -* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html): - Unified implementation of futures and promises which combines features of previous `Future`, - `Promise`, `IVar`, `Event`, `dataflow`, `Delay`, and (partially) `TimerTask` into a single - framework. It extensively uses the new synchronization layer to make all the features - **non-blocking** and **lock-free**, with the exception of obviously blocking operations like - `#wait`, `#value`. It also offers better performance. - -#### Thread-safe Value Objects, Structures, and Collections - -Collection classes that were originally part of the (deprecated) `thread_safe` gem: - -* [Array](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Array.html) A thread-safe - subclass of Ruby's standard [Array](http://ruby-doc.org/core-2.2.0/Array.html). -* [Hash](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Hash.html) A thread-safe - subclass of Ruby's standard [Hash](http://ruby-doc.org/core-2.2.0/Hash.html). -* [Set](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Set.html) A thread-safe - subclass of Ruby's standard [Set](http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html). -* [Map](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Map.html) A hash-like object - that should have much better performance characteristics, especially under high concurrency, - than `Concurrent::Hash`. -* [Tuple](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Tuple.html) A fixed size - array with volatile (synchronized, thread safe) getters/setters. - -Value objects inspired by other languages: - -* [Maybe](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Maybe.html) A thread-safe, - immutable object representing an optional value, based on - [Haskell Data.Maybe](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html). - -Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core-2.2.0/Struct.html): - -* [ImmutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ImmutableStruct.html) - Immutable struct where values are set at construction and cannot be changed later. -* [MutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MutableStruct.html) - Synchronized, mutable struct where values can be safely changed at any time. -* [SettableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/SettableStruct.html) - Synchronized, write-once struct where values can be set at most once, either at construction - or any time thereafter. - -Thread-safe variables: - -* [Agent](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Agent.html): A way to - manage shared, mutable, *asynchronous*, independent state. Based on Clojure's - [Agent](http://clojure.org/agents). -* [Atom](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Atom.html): A way to manage - shared, mutable, *synchronous*, independent state. Based on Clojure's - [Atom](http://clojure.org/atoms). -* [AtomicBoolean](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicBoolean.html) - A boolean value that can be updated atomically. -* [AtomicFixnum](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicFixnum.html) - A numeric value that can be updated atomically. -* [AtomicReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicReference.html) - An object reference that may be updated atomically. -* [Exchanger](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Exchanger.html) - A synchronization point at which threads can pair and swap elements within pairs. Based on - Java's [Exchanger](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html). -* [MVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/MVar.html) A synchronized - single element container. Based on Haskell's - [MVar](https://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Concurrent-MVar.html) and - Scala's [MVar](http://docs.typelevel.org/api/scalaz/nightly/index.html#scalaz.concurrent.MVar$). -* [ThreadLocalVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadLocalVar.html) - A variable where the value is different for each thread. -* [TVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/TVar.html) A transactional - variable implementing software transactional memory (STM). Based on Clojure's - [Ref](http://clojure.org/refs). - -#### Java-inspired ThreadPools and Other Executors - -* See the [thread pool](http://ruby-concurrency.github.io/concurrent-ruby/master/file.thread_pools.html) - overview, which also contains a list of other Executors available. - -#### Thread Synchronization Classes and Algorithms - -* [CountDownLatch](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CountDownLatch.html) - A synchronization object that allows one thread to wait on multiple other threads. -* [CyclicBarrier](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/CyclicBarrier.html) - A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. -* [Event](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Event.html) Old school - kernel-style event. -* [ReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReadWriteLock.html) - A lock that supports multiple readers but only one writer. -* [ReentrantReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ReentrantReadWriteLock.html) - A read/write lock with reentrant and upgrade features. -* [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Semaphore.html) - A counting-based locking mechanism that uses permits. -* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/AtomicMarkableReference.html) - -#### Deprecated - -Deprecated features are still available and bugs are being fixed, but new features will not be added. - -* ~~[Future](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Future.html): - An asynchronous operation that produces a value.~~ Replaced by - [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). - * ~~[.dataflow](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent.html#dataflow-class_method): - Built on Futures, Dataflow allows you to create a task that will be scheduled when all of - its data dependencies are available.~~ Replaced by - [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). -* ~~[Promise](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promise.html): Similar - to Futures, with more features.~~ Replaced by - [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). -* ~~[Delay](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Delay.html) Lazy evaluation - of a block yielding an immutable result. Based on Clojure's - [delay](https://clojuredocs.org/clojure.core/delay).~~ Replaced by - [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). -* ~~[IVar](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/IVar.html) Similar to a - "future" but can be manually assigned once, after which it becomes immutable.~~ Replaced by - [Promises](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises.html). - -### Edge Features - -These are available in the `concurrent-ruby-edge` companion gem. - -These features are under active development and may change frequently. They are expected not to -keep backward compatibility (there may also lack tests and documentation). Semantic versions will -be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move to -`concurrent-ruby` when final. - -* [Actor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Actor.html): Implements - the Actor Model, where concurrent actors exchange messages. - *Status: Partial documentation and tests; depends on new future/promise framework; stability is good.* -* [Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Channel.html): - Communicating Sequential Processes ([CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes)). - Functionally equivalent to Go [channels](https://tour.golang.org/concurrency/2) with additional - inspiration from Clojure [core.async](https://clojure.github.io/core.async/). - *Status: Partial documentation and tests.* -* [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LazyRegister.html) -* [LockFreeLinkedSet](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Edge/LockFreeLinkedSet.html) - *Status: will be moved to core soon.* -* [LockFreeStack](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/LockFreeStack.html) - *Status: missing documentation and tests.* -* [Promises::Channel](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Promises/Channel.html) - A first in first out channel that accepts messages with push family of methods and returns - messages with pop family of methods. - Pop and push operations can be represented as futures, see `#pop_op` and `#push_op`. - The capacity of the channel can be limited to support back pressure, use capacity option in `#initialize`. - `#pop` method blocks ans `#pop_op` returns pending future if there is no message in the channel. - If the capacity is limited the `#push` method blocks and `#push_op` returns pending future. -* [Cancellation](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Cancellation.html) - The Cancellation abstraction provides cooperative cancellation. - - The standard methods `Thread#raise` of `Thread#kill` available in Ruby - are very dangerous (see linked the blog posts bellow). - Therefore concurrent-ruby provides an alternative. - - * - * - * - - It provides an object which represents a task which can be executed, - the task has to get the reference to the object and periodically cooperatively check that it is not cancelled. - Good practices to make tasks cancellable: - * check cancellation every cycle of a loop which does significant work, - * do all blocking actions in a loop with a timeout then on timeout check cancellation - and if ok block again with the timeout -* [Throttle](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Throttle.html) - A tool managing concurrency level of tasks. -* [ErlangActor](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ErlangActor.html) - Actor implementation which precisely matches Erlang actor behaviour. - Requires at least Ruby 2.1 otherwise it's not loaded. - -## Supported Ruby versions - -* MRI 2.0 and above -* JRuby 9000 -* TruffleRuby are supported. -* Any Ruby interpreter that is compliant with Ruby 2.0 or newer. - -Actually we still support mri 1.9.3 and jruby 1.7.27 but we are looking at ways how to drop the support. -Java 8 is preferred for JRuby but every Java version on which JRuby 9000 runs is supported. - -The legacy support for Rubinius is kept but it is no longer maintained, if you would like to help -please respond to [#739](https://github.com/ruby-concurrency/concurrent-ruby/issues/739). - -## Usage - -Everything within this gem can be loaded simply by requiring it: - -```ruby -require 'concurrent' -``` - -*Requiring only specific abstractions from Concurrent Ruby is not yet supported.* - -To use the tools in the Edge gem it must be required separately: - -```ruby -require 'concurrent-edge' -``` - -If the library does not behave as expected, `Concurrent.use_stdlib_logger(Logger::DEBUG)` could -help to reveal the problem. - -## Installation - -```shell -gem install concurrent-ruby -``` - -or add the following line to Gemfile: - -```ruby -gem 'concurrent-ruby', require: 'concurrent' -``` - -and run `bundle install` from your shell. - -### Edge Gem Installation - -The Edge gem must be installed separately from the core gem: - -```shell -gem install concurrent-ruby-edge -``` - -or add the following line to Gemfile: - -```ruby -gem 'concurrent-ruby-edge', require: 'concurrent-edge' -``` - -and run `bundle install` from your shell. - - -### C Extensions for MRI - -Potential performance improvements may be achieved under MRI by installing optional C extensions. -To minimise installation errors the C extensions are available in the `concurrent-ruby-ext` -extension gem. `concurrent-ruby` and `concurrent-ruby-ext` are always released together with same -version. Simply install the extension gem too: - -```ruby -gem install concurrent-ruby-ext -``` - -or add the following line to Gemfile: - -```ruby -gem 'concurrent-ruby-ext' -``` - -and run `bundle install` from your shell. - -In code it is only necessary to - -```ruby -require 'concurrent' -``` - -The `concurrent-ruby` gem will automatically detect the presence of the `concurrent-ruby-ext` gem -and load the appropriate C extensions. - -#### Note For gem developers - -No gems should depend on `concurrent-ruby-ext`. Doing so will force C extensions on your users. The -best practice is to depend on `concurrent-ruby` and let users to decide if they want C extensions. - -## Maintainers - -* [Petr Chalupa](https://github.com/pitr-ch) (lead maintainer, point-of-contact) -* [Jerry D'Antonio](https://github.com/jdantonio) (creator) -* [Chris Seaton](https://github.com/chrisseaton) - -### Special Thanks to - -* [Brian Durand](https://github.com/bdurand) for the `ref` gem -* [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems -* [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem - -and to the past maintainers - -* [Michele Della Torre](https://github.com/mighe) -* [PaweÅ‚ Obrok](https://github.com/obrok) -* [Lucas Allan](https://github.com/lucasallan) - -and to [Ruby Association](https://www.ruby.or.jp/en/) for sponsoring a project -["Enhancing Ruby’s concurrency tooling"](https://www.ruby.or.jp/en/news/20181106) in 2018. - -## License and Copyright - -*Concurrent Ruby* is free software released under the -[MIT License](http://www.opensource.org/licenses/MIT). - -The *Concurrent Ruby* [logo](https://raw.githubusercontent.com/ruby-concurrency/concurrent-ruby/master/docs-source/logo/concurrent-ruby-logo-300x300.png) was -designed by [David Jones](https://twitter.com/zombyboy). It is Copyright © 2014 -[Jerry D'Antonio](https://twitter.com/jerrydantonio). All Rights Reserved. diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Rakefile puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Rakefile --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Rakefile 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/Rakefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,327 +0,0 @@ -require_relative 'lib/concurrent/version' -require_relative 'lib/concurrent/utility/engine' - -if Concurrent.ruby_version :<, 2, 0, 0 - # @!visibility private - module Kernel - def __dir__ - File.dirname __FILE__ - end - end -end - -core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec') -ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec') -edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.gemspec') - -require 'rake/javaextensiontask' - -Rake::JavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext| - ext.ext_dir = 'ext/concurrent-ruby' - ext.lib_dir = 'lib/concurrent' -end - -unless Concurrent.on_jruby? - require 'rake/extensiontask' - - Rake::ExtensionTask.new('concurrent_ruby_ext', ext_gemspec) do |ext| - ext.ext_dir = 'ext/concurrent-ruby-ext' - ext.lib_dir = 'lib/concurrent' - ext.source_pattern = '*.{c,h}' - - ext.cross_compile = true - ext.cross_platform = ['x86-mingw32', 'x64-mingw32'] - end -end - -require 'rake_compiler_dock' -namespace :repackage do - desc '* with Windows fat distributions' - task :all do - Dir.chdir(__dir__) do - # store gems in vendor cache for docker - sh 'bundle package' - - # build only the jar file not the whole gem for java platform, the jar is part the concurrent-ruby-x.y.z.gem - Rake::Task['lib/concurrent/concurrent_ruby.jar'].invoke - - # build all gem files - RakeCompilerDock.sh 'bundle install --local && bundle exec rake cross native package --trace' - end - end -end - -require 'rubygems' -require 'rubygems/package_task' - -Gem::PackageTask.new(core_gemspec) {} if core_gemspec -Gem::PackageTask.new(ext_gemspec) {} if ext_gemspec && !Concurrent.on_jruby? -Gem::PackageTask.new(edge_gemspec) {} if edge_gemspec - -CLEAN.include('lib/concurrent/2.*', 'lib/concurrent/*.jar') - -begin - require 'rspec' - require 'rspec/core/rake_task' - - RSpec::Core::RakeTask.new(:spec) - - namespace :spec do - desc '* Configured for ci' - RSpec::Core::RakeTask.new(:ci) do |t| - options = %w[ --color - --backtrace - --order defined - --format documentation - --tag ~notravis ] - t.rspec_opts = [*options].join(' ') - end - - desc '* test packaged and installed gems instead of local files' - task :installed do - Dir.chdir(__dir__) do - sh "gem install pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" - sh "gem install pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" if Concurrent.on_cruby? - sh "gem install pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" - ENV['NO_PATH'] = 'true' - sh 'bundle update' - sh 'bundle exec rake spec:ci' - end - end - end - - desc 'executed in CI' - task :ci => [:compile, 'spec:ci'] - - task :default => [:clobber, :compile, :spec] -rescue LoadError => e - puts 'RSpec is not installed, skipping test task definitions: ' + e.message -end - -current_yard_version_name = Concurrent::VERSION - -begin - require 'yard' - require 'md_ruby_eval' - require_relative 'support/yard_full_types' - - common_yard_options = ['--no-yardopts', - '--no-document', - '--no-private', - '--embed-mixins', - '--markup', 'markdown', - '--title', 'Concurrent Ruby', - '--template', 'default', - '--template-path', 'yard-template', - '--default-return', 'undocumented'] - - desc 'Generate YARD Documentation (signpost, master)' - task :yard => ['yard:signpost', 'yard:master'] - - namespace :yard do - - desc '* eval markdown files' - task :eval_md do - Dir.chdir File.join(__dir__, 'docs-source') do - sh 'bundle exec md-ruby-eval --auto' - end - end - - task :update_readme do - Dir.chdir __dir__ do - content = File.read(File.join('README.md')). - gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_| - case $1 - when 'LockFreeLinkedSet' - "{Concurrent::Edge::#{$1} #{$1}}" - when '.dataflow' - '{Concurrent.dataflow Concurrent.dataflow}' - when 'thread pool' - '{file:thread_pools.md thread pool}' - else - "{Concurrent::#{$1} #{$1}}" - end - end - FileUtils.mkpath 'tmp' - File.write 'tmp/README.md', content - end - end - - define_yard_task = -> name do - output_dir = "docs/#{name}" - - removal_name = "remove.#{name}" - task removal_name do - Dir.chdir __dir__ do - FileUtils.rm_rf output_dir - end - end - - desc "* of #{name} into subdir #{name}" - YARD::Rake::YardocTask.new(name) do |yard| - yard.options.push( - '--output-dir', output_dir, - '--main', 'tmp/README.md', - *common_yard_options) - yard.files = ['./lib/**/*.rb', - './lib-edge/**/*.rb', - './ext/concurrent_ruby_ext/**/*.c', - '-', - 'docs-source/thread_pools.md', - 'docs-source/promises.out.md', - 'docs-source/medium-example.out.rb', - 'LICENSE.md', - 'CHANGELOG.md'] - end - Rake::Task[name].prerequisites.push removal_name, 'yard:eval_md', 'yard:update_readme' - end - - define_yard_task.call current_yard_version_name - define_yard_task.call 'master' - - desc "* signpost for versions" - YARD::Rake::YardocTask.new(:signpost) do |yard| - yard.options.push( - '--output-dir', 'docs', - '--main', 'docs-source/signpost.md', - *common_yard_options) - yard.files = ['no-lib'] - end - - define_uptodate_task = -> name do - namespace name do - desc "** ensure that #{name} generated documentation is matching the source code" - task :uptodate do - Dir.chdir(__dir__) do - begin - FileUtils.cp_r 'docs', 'docs-copy', verbose: true - Rake::Task["yard:#{name}"].invoke - sh 'diff -r docs/ docs-copy/' do |ok, res| - unless ok - begin - STDOUT.puts 'Command failed. Continue? (y/n)' - input = STDIN.gets.strip.downcase - end until %w(y n).include?(input) - exit 1 if input == 'n' - end - end - ensure - FileUtils.rm_rf 'docs-copy', verbose: true - end - end - end - end - end - - define_uptodate_task.call current_yard_version_name - define_uptodate_task.call 'master' - end - -rescue LoadError => e - puts 'YARD is not installed, skipping documentation task definitions: ' + e.message -end - -desc 'build, test, and publish the gem' -task :release => ['release:checks', 'release:build', 'release:test', 'release:publish'] - -namespace :release do - # Depends on environment of @pitr-ch - - mri_version = '2.5.1' - jruby_version = 'jruby-9.1.17.1' - - task :checks => "yard:#{current_yard_version_name}:uptodate" do - Dir.chdir(__dir__) do - sh 'test -z "$(git status --porcelain)"' do |ok, res| - unless ok - begin - STDOUT.puts 'Command failed. Continue? (y/n)' - input = STDIN.gets.strip.downcase - end until %w(y n).include?(input) - exit 1 if input == 'n' - end - end - sh 'git fetch' - sh 'test $(git show-ref --verify --hash refs/heads/master) = ' + - '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res| - unless ok - begin - STDOUT.puts 'Command failed. Continue? (y/n)' - input = STDIN.gets.strip.downcase - end until %w(y n).include?(input) - exit 1 if input == 'n' - end - end - end - end - - desc '* build all *.gem files necessary for release' - task :build => [:clobber, 'repackage:all'] - - desc '* test actual installed gems instead of cloned repository on MRI and JRuby' - task :test do - Dir.chdir(__dir__) do - old = ENV['RBENV_VERSION'] - - ENV['RBENV_VERSION'] = mri_version - sh 'rbenv version' - sh 'bundle exec rake spec:installed' - - ENV['RBENV_VERSION'] = jruby_version - sh 'rbenv version' - sh 'bundle exec rake spec:installed' - - puts 'Windows build is untested' - - ENV['RBENV_VERSION'] = old - end - end - - desc '* do all nested steps' - task :publish => ['publish:ask', 'publish:tag', 'publish:rubygems', 'publish:post_steps'] - - namespace :publish do - publish_edge = false - - task :ask do - begin - STDOUT.puts 'Do you want to publish anything? (y/n)' - input = STDIN.gets.strip.downcase - end until %w(y n).include?(input) - exit 1 if input == 'n' - begin - STDOUT.puts 'Do you want to publish edge? (y/n)' - input = STDIN.gets.strip.downcase - end until %w(y n).include?(input) - publish_edge = input == 'y' - end - - desc '** tag HEAD with current version and push to github' - task :tag do - Dir.chdir(__dir__) do - sh "git tag v#{Concurrent::VERSION}" - sh "git push origin v#{Concurrent::VERSION}" - sh "git tag edge-v#{Concurrent::EDGE_VERSION}" if publish_edge - sh "git push origin edge-v#{Concurrent::EDGE_VERSION}" if publish_edge - end - end - - desc '** push all *.gem files to rubygems' - task :rubygems do - Dir.chdir(__dir__) do - sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" - sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" if publish_edge - sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" - sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x64-mingw32.gem" - sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x86-mingw32.gem" - end - end - - desc '** print post release steps' - task :post_steps do - puts 'Manually: create a release on GitHub with relevant changelog part' - puts 'Manually: send email same as release with relevant changelog part' - puts 'Manually: tweet' - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/ConcurrentRubyService.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -import org.jruby.Ruby; -import org.jruby.runtime.load.BasicLibraryService; - -import java.io.IOException; - -public class ConcurrentRubyService implements BasicLibraryService { - - public boolean basicLoad(final Ruby runtime) throws IOException { - new com.concurrent_ruby.ext.AtomicReferenceLibrary().load(runtime, false); - new com.concurrent_ruby.ext.JavaAtomicBooleanLibrary().load(runtime, false); - new com.concurrent_ruby.ext.JavaAtomicFixnumLibrary().load(runtime, false); - new com.concurrent_ruby.ext.JavaSemaphoreLibrary().load(runtime, false); - new com.concurrent_ruby.ext.SynchronizationLibrary().load(runtime, false); - new com.concurrent_ruby.ext.JRubyMapBackendLibrary().load(runtime, false); - return true; - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -package com.concurrent_ruby.ext; - -import java.lang.reflect.Field; -import java.io.IOException; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import org.jruby.Ruby; -import org.jruby.RubyClass; -import org.jruby.RubyModule; -import org.jruby.RubyNumeric; -import org.jruby.RubyObject; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.load.Library; - -/** - * This library adds an atomic reference type to JRuby for use in the atomic - * library. We do a native version to avoid the implicit value coercion that - * normally happens through JI. - * - * @author headius - */ -public class AtomicReferenceLibrary implements Library { - public void load(Ruby runtime, boolean wrap) throws IOException { - RubyModule concurrentMod = runtime.defineModule("Concurrent"); - RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); - try { - sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class); - atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR); - } catch (Exception e) { - // leave it as Java 6/7 version - } - atomicCls.defineAnnotatedMethods(JRubyReference.class); - } - - private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JRubyReference(runtime, klazz); - } - }; - - private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JRubyReference8(runtime, klazz); - } - }; - - @JRubyClass(name="JRubyReference", parent="Object") - public static class JRubyReference extends RubyObject { - volatile IRubyObject reference; - - static final sun.misc.Unsafe UNSAFE; - static final long referenceOffset; - - static { - try { - UNSAFE = UnsafeHolder.U; - Class k = JRubyReference.class; - referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference")); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public JRubyReference(Ruby runtime, RubyClass klass) { - super(runtime, klass); - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context) { - UNSAFE.putObject(this, referenceOffset, context.nil); - return context.nil; - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context, IRubyObject value) { - UNSAFE.putObject(this, referenceOffset, value); - return context.nil; - } - - @JRubyMethod(name = {"get", "value"}) - public IRubyObject get() { - return reference; - } - - @JRubyMethod(name = {"set", "value="}) - public IRubyObject set(IRubyObject newValue) { - UNSAFE.putObjectVolatile(this, referenceOffset, newValue); - return newValue; - } - - @JRubyMethod(name = {"compare_and_set", "compare_and_swap"}) - public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { - Ruby runtime = context.runtime; - - if (expectedValue instanceof RubyNumeric) { - // numerics are not always idempotent in Ruby, so we need to do slower logic - return compareAndSetNumeric(context, expectedValue, newValue); - } - - return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue)); - } - - @JRubyMethod(name = {"get_and_set", "swap"}) - public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { - // less-efficient version for Java 6 and 7 - while (true) { - IRubyObject oldValue = get(); - if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) { - return oldValue; - } - } - } - - private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) { - Ruby runtime = context.runtime; - - // loop until: - // * reference CAS would succeed for same-valued objects - // * current and expected have different values as determined by #equals - while (true) { - IRubyObject current = reference; - - if (!(current instanceof RubyNumeric)) { - // old value is not numeric, CAS fails - return runtime.getFalse(); - } - - RubyNumeric currentNumber = (RubyNumeric)current; - if (!currentNumber.equals(expectedValue)) { - // current number does not equal expected, fail CAS - return runtime.getFalse(); - } - - // check that current has not changed, or else allow loop to repeat - boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue); - if (success) { - // value is same and did not change in interim...success - return runtime.getTrue(); - } - } - } - } - - private static final class UnsafeHolder { - private UnsafeHolder(){} - - public static final sun.misc.Unsafe U = loadUnsafe(); - - private static sun.misc.Unsafe loadUnsafe() { - try { - Class unsafeClass = Class.forName("sun.misc.Unsafe"); - Field f = unsafeClass.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return (sun.misc.Unsafe) f.get(null); - } catch (Exception e) { - return null; - } - } - } - - public static class JRubyReference8 extends JRubyReference { - public JRubyReference8(Ruby runtime, RubyClass klass) { - super(runtime, klass); - } - - @Override - public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) { - // efficient version for Java 8 - return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue); - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,248 +0,0 @@ -package com.concurrent_ruby.ext; - -import org.jruby.*; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap; -import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8; -import com.concurrent_ruby.ext.jsr166e.nounsafe.*; -import org.jruby.runtime.Block; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.load.Library; - -import java.io.IOException; -import java.util.Map; - -import static org.jruby.runtime.Visibility.PRIVATE; - -/** - * Native Java implementation to avoid the JI overhead. - * - * @author thedarkone - */ -public class JRubyMapBackendLibrary implements Library { - public void load(Ruby runtime, boolean wrap) throws IOException { - - RubyModule concurrentMod = runtime.defineModule("Concurrent"); - RubyModule thread_safeMod = concurrentMod.defineModuleUnder("Collection"); - RubyClass jrubyRefClass = thread_safeMod.defineClassUnder("JRubyMapBackend", runtime.getObject(), BACKEND_ALLOCATOR); - jrubyRefClass.setAllocator(BACKEND_ALLOCATOR); - jrubyRefClass.defineAnnotatedMethods(JRubyMapBackend.class); - } - - private static final ObjectAllocator BACKEND_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JRubyMapBackend(runtime, klazz); - } - }; - - @JRubyClass(name="JRubyMapBackend", parent="Object") - public static class JRubyMapBackend extends RubyObject { - // Defaults used by the CHM - static final int DEFAULT_INITIAL_CAPACITY = 16; - static final float DEFAULT_LOAD_FACTOR = 0.75f; - - public static final boolean CAN_USE_UNSAFE_CHM = canUseUnsafeCHM(); - - private ConcurrentHashMap map; - - private static ConcurrentHashMap newCHM(int initialCapacity, float loadFactor) { - if (CAN_USE_UNSAFE_CHM) { - return new ConcurrentHashMapV8(initialCapacity, loadFactor); - } else { - return new com.concurrent_ruby.ext.jsr166e.nounsafe.ConcurrentHashMapV8(initialCapacity, loadFactor); - } - } - - private static ConcurrentHashMap newCHM() { - return newCHM(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); - } - - private static boolean canUseUnsafeCHM() { - try { - new com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8(); // force class load and initialization - return true; - } catch (Throwable t) { // ensuring we really do catch everything - // Doug's Unsafe setup errors always have this "Could not ini.." message - if (isCausedBySecurityException(t)) { - return false; - } - throw (t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t)); - } - } - - private static boolean isCausedBySecurityException(Throwable t) { - while (t != null) { - if ((t.getMessage() != null && t.getMessage().contains("Could not initialize intrinsics")) || t instanceof SecurityException) { - return true; - } - t = t.getCause(); - } - return false; - } - - public JRubyMapBackend(Ruby runtime, RubyClass klass) { - super(runtime, klass); - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context) { - map = newCHM(); - return context.getRuntime().getNil(); - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context, IRubyObject options) { - map = toCHM(context, options); - return context.getRuntime().getNil(); - } - - private ConcurrentHashMap toCHM(ThreadContext context, IRubyObject options) { - Ruby runtime = context.getRuntime(); - if (!options.isNil() && options.respondsTo("[]")) { - IRubyObject rInitialCapacity = options.callMethod(context, "[]", runtime.newSymbol("initial_capacity")); - IRubyObject rLoadFactor = options.callMethod(context, "[]", runtime.newSymbol("load_factor")); - int initialCapacity = !rInitialCapacity.isNil() ? RubyNumeric.num2int(rInitialCapacity.convertToInteger()) : DEFAULT_INITIAL_CAPACITY; - float loadFactor = !rLoadFactor.isNil() ? (float)RubyNumeric.num2dbl(rLoadFactor.convertToFloat()) : DEFAULT_LOAD_FACTOR; - return newCHM(initialCapacity, loadFactor); - } else { - return newCHM(); - } - } - - @JRubyMethod(name = "[]", required = 1) - public IRubyObject op_aref(ThreadContext context, IRubyObject key) { - IRubyObject value; - return ((value = map.get(key)) == null) ? context.getRuntime().getNil() : value; - } - - @JRubyMethod(name = {"[]="}, required = 2) - public IRubyObject op_aset(IRubyObject key, IRubyObject value) { - map.put(key, value); - return value; - } - - @JRubyMethod - public IRubyObject put_if_absent(IRubyObject key, IRubyObject value) { - IRubyObject result = map.putIfAbsent(key, value); - return result == null ? getRuntime().getNil() : result; - } - - @JRubyMethod - public IRubyObject compute_if_absent(final ThreadContext context, final IRubyObject key, final Block block) { - return map.computeIfAbsent(key, new ConcurrentHashMap.Fun() { - @Override - public IRubyObject apply(IRubyObject key) { - return block.yieldSpecific(context); - } - }); - } - - @JRubyMethod - public IRubyObject compute_if_present(final ThreadContext context, final IRubyObject key, final Block block) { - IRubyObject result = map.computeIfPresent(key, new ConcurrentHashMap.BiFun() { - @Override - public IRubyObject apply(IRubyObject key, IRubyObject oldValue) { - IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); - return result.isNil() ? null : result; - } - }); - return result == null ? context.getRuntime().getNil() : result; - } - - @JRubyMethod - public IRubyObject compute(final ThreadContext context, final IRubyObject key, final Block block) { - IRubyObject result = map.compute(key, new ConcurrentHashMap.BiFun() { - @Override - public IRubyObject apply(IRubyObject key, IRubyObject oldValue) { - IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); - return result.isNil() ? null : result; - } - }); - return result == null ? context.getRuntime().getNil() : result; - } - - @JRubyMethod - public IRubyObject merge_pair(final ThreadContext context, final IRubyObject key, final IRubyObject value, final Block block) { - IRubyObject result = map.merge(key, value, new ConcurrentHashMap.BiFun() { - @Override - public IRubyObject apply(IRubyObject oldValue, IRubyObject newValue) { - IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue); - return result.isNil() ? null : result; - } - }); - return result == null ? context.getRuntime().getNil() : result; - } - - @JRubyMethod - public RubyBoolean replace_pair(IRubyObject key, IRubyObject oldValue, IRubyObject newValue) { - return getRuntime().newBoolean(map.replace(key, oldValue, newValue)); - } - - @JRubyMethod(name = "key?", required = 1) - public RubyBoolean has_key_p(IRubyObject key) { - return map.containsKey(key) ? getRuntime().getTrue() : getRuntime().getFalse(); - } - - @JRubyMethod - public IRubyObject key(IRubyObject value) { - final IRubyObject key = map.findKey(value); - return key == null ? getRuntime().getNil() : key; - } - - @JRubyMethod - public IRubyObject replace_if_exists(IRubyObject key, IRubyObject value) { - IRubyObject result = map.replace(key, value); - return result == null ? getRuntime().getNil() : result; - } - - @JRubyMethod - public IRubyObject get_and_set(IRubyObject key, IRubyObject value) { - IRubyObject result = map.put(key, value); - return result == null ? getRuntime().getNil() : result; - } - - @JRubyMethod - public IRubyObject delete(IRubyObject key) { - IRubyObject result = map.remove(key); - return result == null ? getRuntime().getNil() : result; - } - - @JRubyMethod - public RubyBoolean delete_pair(IRubyObject key, IRubyObject value) { - return getRuntime().newBoolean(map.remove(key, value)); - } - - @JRubyMethod - public IRubyObject clear() { - map.clear(); - return this; - } - - @JRubyMethod - public IRubyObject each_pair(ThreadContext context, Block block) { - for (Map.Entry entry : map.entrySet()) { - block.yieldSpecific(context, entry.getKey(), entry.getValue()); - } - return this; - } - - @JRubyMethod - public RubyFixnum size(ThreadContext context) { - return context.getRuntime().newFixnum(map.size()); - } - - @JRubyMethod - public IRubyObject get_or_default(IRubyObject key, IRubyObject defaultValue) { - return map.getValueOrDefault(key, defaultValue); - } - - @JRubyMethod(visibility = PRIVATE) - public JRubyMapBackend initialize_copy(ThreadContext context, IRubyObject other) { - map = newCHM(); - return this; - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -package com.concurrent_ruby.ext; - -import org.jruby.Ruby; -import org.jruby.RubyBoolean; -import org.jruby.RubyClass; -import org.jruby.RubyModule; -import org.jruby.RubyNil; -import org.jruby.RubyObject; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.load.Library; - -import java.io.IOException; -import java.util.concurrent.atomic.AtomicBoolean; - -public class JavaAtomicBooleanLibrary implements Library { - - public void load(Ruby runtime, boolean wrap) throws IOException { - RubyModule concurrentMod = runtime.defineModule("Concurrent"); - RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicBoolean", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); - atomicCls.defineAnnotatedMethods(JavaAtomicBoolean.class); - } - - private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JavaAtomicBoolean(runtime, klazz); - } - }; - - @JRubyClass(name = "JavaAtomicBoolean", parent = "Object") - public static class JavaAtomicBoolean extends RubyObject { - - private AtomicBoolean atomicBoolean; - - public JavaAtomicBoolean(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context, IRubyObject value) { - atomicBoolean = new AtomicBoolean(convertRubyBooleanToJavaBoolean(value)); - return context.nil; - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context) { - atomicBoolean = new AtomicBoolean(); - return context.nil; - } - - @JRubyMethod(name = "value") - public IRubyObject value() { - return getRuntime().newBoolean(atomicBoolean.get()); - } - - @JRubyMethod(name = "true?") - public IRubyObject isAtomicTrue() { - return getRuntime().newBoolean(atomicBoolean.get()); - } - - @JRubyMethod(name = "false?") - public IRubyObject isAtomicFalse() { - return getRuntime().newBoolean((atomicBoolean.get() == false)); - } - - @JRubyMethod(name = "value=") - public IRubyObject setAtomic(ThreadContext context, IRubyObject newValue) { - atomicBoolean.set(convertRubyBooleanToJavaBoolean(newValue)); - return context.nil; - } - - @JRubyMethod(name = "make_true") - public IRubyObject makeTrue() { - return getRuntime().newBoolean(atomicBoolean.compareAndSet(false, true)); - } - - @JRubyMethod(name = "make_false") - public IRubyObject makeFalse() { - return getRuntime().newBoolean(atomicBoolean.compareAndSet(true, false)); - } - - private boolean convertRubyBooleanToJavaBoolean(IRubyObject newValue) { - if (newValue instanceof RubyBoolean.False || newValue instanceof RubyNil) { - return false; - } else { - return true; - } - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -package com.concurrent_ruby.ext; - -import java.io.IOException; -import java.util.concurrent.atomic.AtomicLong; -import org.jruby.Ruby; -import org.jruby.RubyClass; -import org.jruby.RubyFixnum; -import org.jruby.RubyModule; -import org.jruby.RubyObject; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.load.Library; -import org.jruby.runtime.Block; - -public class JavaAtomicFixnumLibrary implements Library { - - public void load(Ruby runtime, boolean wrap) throws IOException { - RubyModule concurrentMod = runtime.defineModule("Concurrent"); - RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicFixnum", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); - - atomicCls.defineAnnotatedMethods(JavaAtomicFixnum.class); - } - - private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JavaAtomicFixnum(runtime, klazz); - } - }; - - @JRubyClass(name = "JavaAtomicFixnum", parent = "Object") - public static class JavaAtomicFixnum extends RubyObject { - - private AtomicLong atomicLong; - - public JavaAtomicFixnum(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context) { - this.atomicLong = new AtomicLong(0); - return context.nil; - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context, IRubyObject value) { - this.atomicLong = new AtomicLong(rubyFixnumToLong(value)); - return context.nil; - } - - @JRubyMethod(name = "value") - public IRubyObject getValue() { - return getRuntime().newFixnum(atomicLong.get()); - } - - @JRubyMethod(name = "value=") - public IRubyObject setValue(ThreadContext context, IRubyObject newValue) { - atomicLong.set(rubyFixnumToLong(newValue)); - return context.nil; - } - - @JRubyMethod(name = {"increment", "up"}) - public IRubyObject increment() { - return getRuntime().newFixnum(atomicLong.incrementAndGet()); - } - - @JRubyMethod(name = {"increment", "up"}) - public IRubyObject increment(IRubyObject value) { - long delta = rubyFixnumToLong(value); - return getRuntime().newFixnum(atomicLong.addAndGet(delta)); - } - - @JRubyMethod(name = {"decrement", "down"}) - public IRubyObject decrement() { - return getRuntime().newFixnum(atomicLong.decrementAndGet()); - } - - @JRubyMethod(name = {"decrement", "down"}) - public IRubyObject decrement(IRubyObject value) { - long delta = rubyFixnumToLong(value); - return getRuntime().newFixnum(atomicLong.addAndGet(-delta)); - } - - @JRubyMethod(name = "compare_and_set") - public IRubyObject compareAndSet(ThreadContext context, IRubyObject expect, IRubyObject update) { - return getRuntime().newBoolean(atomicLong.compareAndSet(rubyFixnumToLong(expect), rubyFixnumToLong(update))); - } - - @JRubyMethod - public IRubyObject update(ThreadContext context, Block block) { - for (;;) { - long _oldValue = atomicLong.get(); - IRubyObject oldValue = getRuntime().newFixnum(_oldValue); - IRubyObject newValue = block.yield(context, oldValue); - if (atomicLong.compareAndSet(_oldValue, rubyFixnumToLong(newValue))) { - return newValue; - } - } - } - - private long rubyFixnumToLong(IRubyObject value) { - if (value instanceof RubyFixnum) { - RubyFixnum fixNum = (RubyFixnum) value; - return fixNum.getLongValue(); - } else { - throw getRuntime().newArgumentError("value must be a Fixnum"); - } - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -package com.concurrent_ruby.ext; - -import java.io.IOException; -import java.util.concurrent.Semaphore; -import org.jruby.Ruby; -import org.jruby.RubyClass; -import org.jruby.RubyFixnum; -import org.jruby.RubyModule; -import org.jruby.RubyNumeric; -import org.jruby.RubyObject; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; - -public class JavaSemaphoreLibrary { - - public void load(Ruby runtime, boolean wrap) throws IOException { - RubyModule concurrentMod = runtime.defineModule("Concurrent"); - RubyClass atomicCls = concurrentMod.defineClassUnder("JavaSemaphore", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR); - - atomicCls.defineAnnotatedMethods(JavaSemaphore.class); - } - - private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JavaSemaphore(runtime, klazz); - } - }; - - @JRubyClass(name = "JavaSemaphore", parent = "Object") - public static class JavaSemaphore extends RubyObject { - - private JRubySemaphore semaphore; - - public JavaSemaphore(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - - @JRubyMethod - public IRubyObject initialize(ThreadContext context, IRubyObject value) { - this.semaphore = new JRubySemaphore(rubyFixnumInt(value, "count")); - return context.nil; - } - - @JRubyMethod - public IRubyObject acquire(ThreadContext context, IRubyObject value) throws InterruptedException { - this.semaphore.acquire(rubyFixnumToPositiveInt(value, "permits")); - return context.nil; - } - - @JRubyMethod(name = "available_permits") - public IRubyObject availablePermits(ThreadContext context) { - return getRuntime().newFixnum(this.semaphore.availablePermits()); - } - - @JRubyMethod(name = "drain_permits") - public IRubyObject drainPermits(ThreadContext context) { - return getRuntime().newFixnum(this.semaphore.drainPermits()); - } - - @JRubyMethod - public IRubyObject acquire(ThreadContext context) throws InterruptedException { - this.semaphore.acquire(1); - return context.nil; - } - - @JRubyMethod(name = "try_acquire") - public IRubyObject tryAcquire(ThreadContext context) throws InterruptedException { - return getRuntime().newBoolean(semaphore.tryAcquire(1)); - } - - @JRubyMethod(name = "try_acquire") - public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits) throws InterruptedException { - return getRuntime().newBoolean(semaphore.tryAcquire(rubyFixnumToPositiveInt(permits, "permits"))); - } - - @JRubyMethod(name = "try_acquire") - public IRubyObject tryAcquire(ThreadContext context, IRubyObject permits, IRubyObject timeout) throws InterruptedException { - return getRuntime().newBoolean( - semaphore.tryAcquire( - rubyFixnumToPositiveInt(permits, "permits"), - rubyNumericToLong(timeout, "timeout"), - java.util.concurrent.TimeUnit.SECONDS) - ); - } - - @JRubyMethod - public IRubyObject release(ThreadContext context) { - this.semaphore.release(1); - return getRuntime().newBoolean(true); - } - - @JRubyMethod - public IRubyObject release(ThreadContext context, IRubyObject value) { - this.semaphore.release(rubyFixnumToPositiveInt(value, "permits")); - return getRuntime().newBoolean(true); - } - - @JRubyMethod(name = "reduce_permits") - public IRubyObject reducePermits(ThreadContext context, IRubyObject reduction) throws InterruptedException { - this.semaphore.publicReducePermits(rubyFixnumToNonNegativeInt(reduction, "reduction")); - return context.nil; - } - - private int rubyFixnumInt(IRubyObject value, String paramName) { - if (value instanceof RubyFixnum) { - RubyFixnum fixNum = (RubyFixnum) value; - return (int) fixNum.getLongValue(); - } else { - throw getRuntime().newArgumentError(paramName + " must be integer"); - } - } - - private int rubyFixnumToNonNegativeInt(IRubyObject value, String paramName) { - if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() >= 0) { - RubyFixnum fixNum = (RubyFixnum) value; - return (int) fixNum.getLongValue(); - } else { - throw getRuntime().newArgumentError(paramName + " must be a non-negative integer"); - } - } - - private int rubyFixnumToPositiveInt(IRubyObject value, String paramName) { - if (value instanceof RubyFixnum && ((RubyFixnum) value).getLongValue() > 0) { - RubyFixnum fixNum = (RubyFixnum) value; - return (int) fixNum.getLongValue(); - } else { - throw getRuntime().newArgumentError(paramName + " must be an integer greater than zero"); - } - } - - private long rubyNumericToLong(IRubyObject value, String paramName) { - if (value instanceof RubyNumeric && ((RubyNumeric) value).getDoubleValue() > 0) { - RubyNumeric fixNum = (RubyNumeric) value; - return fixNum.getLongValue(); - } else { - throw getRuntime().newArgumentError(paramName + " must be a float greater than zero"); - } - } - - class JRubySemaphore extends Semaphore { - - public JRubySemaphore(int permits) { - super(permits); - } - - public JRubySemaphore(int permits, boolean value) { - super(permits, value); - } - - public void publicReducePermits(int i) { - reducePermits(i); - } - - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,307 +0,0 @@ -package com.concurrent_ruby.ext; - -import org.jruby.Ruby; -import org.jruby.RubyBasicObject; -import org.jruby.RubyClass; -import org.jruby.RubyModule; -import org.jruby.RubyObject; -import org.jruby.RubyThread; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.runtime.Block; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.Visibility; -import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.load.Library; -import sun.misc.Unsafe; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -public class SynchronizationLibrary implements Library { - - private static final Unsafe UNSAFE = loadUnsafe(); - private static final boolean FULL_FENCE = supportsFences(); - - private static Unsafe loadUnsafe() { - try { - Class ncdfe = Class.forName("sun.misc.Unsafe"); - Field f = ncdfe.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return (Unsafe) f.get((java.lang.Object) null); - } catch (Exception var2) { - return null; - } catch (NoClassDefFoundError var3) { - return null; - } - } - - private static boolean supportsFences() { - if (UNSAFE == null) { - return false; - } else { - try { - Method m = UNSAFE.getClass().getDeclaredMethod("fullFence", new Class[0]); - if (m != null) { - return true; - } - } catch (Exception var1) { - // nothing - } - - return false; - } - } - - private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JRubyObject(runtime, klazz); - } - }; - - private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new Object(runtime, klazz); - } - }; - - private static final ObjectAllocator ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new AbstractLockableObject(runtime, klazz); - } - }; - - private static final ObjectAllocator JRUBY_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() { - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JRubyLockableObject(runtime, klazz); - } - }; - - public void load(Ruby runtime, boolean wrap) throws IOException { - RubyModule synchronizationModule = runtime. - defineModule("Concurrent"). - defineModuleUnder("Synchronization"); - - RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile"); - jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class); - - defineClass(runtime, synchronizationModule, "AbstractObject", "JRubyObject", - JRubyObject.class, JRUBY_OBJECT_ALLOCATOR); - - defineClass(runtime, synchronizationModule, "JRubyObject", "Object", - Object.class, OBJECT_ALLOCATOR); - - defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject", - AbstractLockableObject.class, ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR); - - defineClass(runtime, synchronizationModule, "AbstractLockableObject", "JRubyLockableObject", - JRubyLockableObject.class, JRUBY_LOCKABLE_OBJECT_ALLOCATOR); - - defineClass(runtime, synchronizationModule, "Object", "JRuby", - JRuby.class, new ObjectAllocator() { - @Override - public IRubyObject allocate(Ruby runtime, RubyClass klazz) { - return new JRuby(runtime, klazz); - } - }); - } - - private RubyClass defineClass( - Ruby runtime, - RubyModule namespace, - String parentName, - String name, - Class javaImplementation, - ObjectAllocator allocator) { - final RubyClass parentClass = namespace.getClass(parentName); - - if (parentClass == null) { - System.out.println("not found " + parentName); - throw runtime.newRuntimeError(namespace.toString() + "::" + parentName + " is missing"); - } - - final RubyClass newClass = namespace.defineClassUnder(name, parentClass, allocator); - newClass.defineAnnotatedMethods(javaImplementation); - return newClass; - } - - // Facts: - // - all ivar reads are without any synchronisation of fences see - // https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/VariableAccessor.java#L110-110 - // - writes depend on UnsafeHolder.U, null -> SynchronizedVariableAccessor, !null -> StampedVariableAccessor - // SynchronizedVariableAccessor wraps with synchronized block, StampedVariableAccessor uses fullFence or - // volatilePut - // TODO (pitr 16-Sep-2015): what do we do in Java 9 ? - - // module JRubyAttrVolatile - public static class JRubyAttrVolatile { - - // volatile threadContext is used as a memory barrier per the JVM memory model happens-before semantic - // on volatile fields. any volatile field could have been used but using the thread context is an - // attempt to avoid code elimination. - private static volatile int volatileField; - - @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC) - public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) { - // Prevent reordering of ivar writes with publication of this instance - if (!FULL_FENCE) { - // Assuming that following volatile read and write is not eliminated it simulates fullFence. - // If it's eliminated it'll cause problems only on non-x86 platforms. - // http://shipilev.net/blog/2014/jmm-pragmatics/#_happens_before_test_your_understanding - final int volatileRead = volatileField; - volatileField = context.getLine(); - } else { - UNSAFE.fullFence(); - } - return context.nil; - } - - @JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC) - public static IRubyObject instanceVariableGetVolatile( - ThreadContext context, - IRubyObject self, - IRubyObject name) { - // Ensure we ses latest value with loadFence - if (!FULL_FENCE) { - // piggybacking on volatile read, simulating loadFence - final int volatileRead = volatileField; - return ((RubyBasicObject) self).instance_variable_get(context, name); - } else { - UNSAFE.loadFence(); - return ((RubyBasicObject) self).instance_variable_get(context, name); - } - } - - @JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC) - public static IRubyObject InstanceVariableSetVolatile( - ThreadContext context, - IRubyObject self, - IRubyObject name, - IRubyObject value) { - // Ensure we make last update visible - if (!FULL_FENCE) { - // piggybacking on volatile write, simulating storeFence - final IRubyObject result = ((RubyBasicObject) self).instance_variable_set(name, value); - volatileField = context.getLine(); - return result; - } else { - // JRuby uses StampedVariableAccessor which calls fullFence - // so no additional steps needed. - // See https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/StampedVariableAccessor.java#L151-L159 - return ((RubyBasicObject) self).instance_variable_set(name, value); - } - } - } - - @JRubyClass(name = "JRubyObject", parent = "AbstractObject") - public static class JRubyObject extends RubyObject { - - public JRubyObject(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - } - - @JRubyClass(name = "Object", parent = "JRubyObject") - public static class Object extends JRubyObject { - - public Object(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - } - - @JRubyClass(name = "AbstractLockableObject", parent = "Object") - public static class AbstractLockableObject extends Object { - - public AbstractLockableObject(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - } - - @JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject") - public static class JRubyLockableObject extends JRubyObject { - - public JRubyLockableObject(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - - @JRubyMethod(name = "synchronize", visibility = Visibility.PROTECTED) - public IRubyObject rubySynchronize(ThreadContext context, Block block) { - synchronized (this) { - return block.yield(context, null); - } - } - - @JRubyMethod(name = "ns_wait", optional = 1, visibility = Visibility.PROTECTED) - public IRubyObject nsWait(ThreadContext context, IRubyObject[] args) { - Ruby runtime = context.runtime; - if (args.length > 1) { - throw runtime.newArgumentError(args.length, 1); - } - Double timeout = null; - if (args.length > 0 && !args[0].isNil()) { - timeout = args[0].convertToFloat().getDoubleValue(); - if (timeout < 0) { - throw runtime.newArgumentError("time interval must be positive"); - } - } - if (Thread.interrupted()) { - throw runtime.newConcurrencyError("thread interrupted"); - } - boolean success = false; - try { - success = context.getThread().wait_timeout(this, timeout); - } catch (InterruptedException ie) { - throw runtime.newConcurrencyError(ie.getLocalizedMessage()); - } finally { - // An interrupt or timeout may have caused us to miss - // a notify that we consumed, so do another notify in - // case someone else is available to pick it up. - if (!success) { - this.notify(); - } - } - return this; - } - - @JRubyMethod(name = "ns_signal", visibility = Visibility.PROTECTED) - public IRubyObject nsSignal(ThreadContext context) { - notify(); - return this; - } - - @JRubyMethod(name = "ns_broadcast", visibility = Visibility.PROTECTED) - public IRubyObject nsBroadcast(ThreadContext context) { - notifyAll(); - return this; - } - } - - @JRubyClass(name = "JRuby") - public static class JRuby extends RubyObject { - public JRuby(Ruby runtime, RubyClass metaClass) { - super(runtime, metaClass); - } - - @JRubyMethod(name = "sleep_interruptibly", visibility = Visibility.PUBLIC, module = true) - public static IRubyObject sleepInterruptibly(final ThreadContext context, IRubyObject receiver, final Block block) { - try { - context.getThread().executeBlockingTask(new RubyThread.BlockingTask() { - @Override - public void run() throws InterruptedException { - block.call(context); - } - - @Override - public void wakeup() { - context.getThread().getNativeThread().interrupt(); - } - }); - } catch (InterruptedException e) { - throw context.runtime.newThreadError("interrupted in Concurrent::Synchronization::JRuby.sleep_interruptibly"); - } - return context.nil; - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -package com.concurrent_ruby.ext.jsr166e; - -import java.util.Map; -import java.util.Set; - -public interface ConcurrentHashMap { - /** Interface describing a function of one argument */ - public interface Fun { T apply(A a); } - /** Interface describing a function of two arguments */ - public interface BiFun { T apply(A a, B b); } - - public V get(K key); - public V put(K key, V value); - public V putIfAbsent(K key, V value); - public V computeIfAbsent(K key, Fun mf); - public V computeIfPresent(K key, BiFun mf); - public V compute(K key, BiFun mf); - public V merge(K key, V value, BiFun mf); - public boolean replace(K key, V oldVal, V newVal); - public V replace(K key, V value); - public boolean containsKey(K key); - public boolean remove(Object key, Object value); - public V remove(K key); - public void clear(); - public Set> entrySet(); - public int size(); - public V getValueOrDefault(Object key, V defaultValue); - - public boolean containsValue(V value); - public K findKey(V value); -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,3863 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on the 1.79 version. - -package com.concurrent_ruby.ext.jsr166e; - -import org.jruby.RubyClass; -import org.jruby.RubyNumeric; -import org.jruby.RubyObject; -import org.jruby.exceptions.RaiseException; -import com.concurrent_ruby.ext.jsr166y.ThreadLocalRandom; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; - -import java.util.Arrays; -import java.util.Map; -import java.util.Set; -import java.util.Collection; -import java.util.Hashtable; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Enumeration; -import java.util.ConcurrentModificationException; -import java.util.NoSuchElementException; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.locks.AbstractQueuedSynchronizer; - -import java.io.Serializable; - -/** - * A hash table supporting full concurrency of retrievals and - * high expected concurrency for updates. This class obeys the - * same functional specification as {@link java.util.Hashtable}, and - * includes versions of methods corresponding to each method of - * {@code Hashtable}. However, even though all operations are - * thread-safe, retrieval operations do not entail locking, - * and there is not any support for locking the entire table - * in a way that prevents all access. This class is fully - * interoperable with {@code Hashtable} in programs that rely on its - * thread safety but not on its synchronization details. - * - *

Retrieval operations (including {@code get}) generally do not - * block, so may overlap with update operations (including {@code put} - * and {@code remove}). Retrievals reflect the results of the most - * recently completed update operations holding upon their - * onset. (More formally, an update operation for a given key bears a - * happens-before relation with any (non-null) retrieval for - * that key reporting the updated value.) For aggregate operations - * such as {@code putAll} and {@code clear}, concurrent retrievals may - * reflect insertion or removal of only some entries. Similarly, - * Iterators and Enumerations return elements reflecting the state of - * the hash table at some point at or since the creation of the - * iterator/enumeration. They do not throw {@link - * ConcurrentModificationException}. However, iterators are designed - * to be used by only one thread at a time. Bear in mind that the - * results of aggregate status methods including {@code size}, {@code - * isEmpty}, and {@code containsValue} are typically useful only when - * a map is not undergoing concurrent updates in other threads. - * Otherwise the results of these methods reflect transient states - * that may be adequate for monitoring or estimation purposes, but not - * for program control. - * - *

The table is dynamically expanded when there are too many - * collisions (i.e., keys that have distinct hash codes but fall into - * the same slot modulo the table size), with the expected average - * effect of maintaining roughly two bins per mapping (corresponding - * to a 0.75 load factor threshold for resizing). There may be much - * variance around this average as mappings are added and removed, but - * overall, this maintains a commonly accepted time/space tradeoff for - * hash tables. However, resizing this or any other kind of hash - * table may be a relatively slow operation. When possible, it is a - * good idea to provide a size estimate as an optional {@code - * initialCapacity} constructor argument. An additional optional - * {@code loadFactor} constructor argument provides a further means of - * customizing initial table capacity by specifying the table density - * to be used in calculating the amount of space to allocate for the - * given number of elements. Also, for compatibility with previous - * versions of this class, constructors may optionally specify an - * expected {@code concurrencyLevel} as an additional hint for - * internal sizing. Note that using many keys with exactly the same - * {@code hashCode()} is a sure way to slow down performance of any - * hash table. - * - *

A {@link Set} projection of a ConcurrentHashMapV8 may be created - * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed - * (using {@link #keySet(Object)} when only keys are of interest, and the - * mapped values are (perhaps transiently) not used or all take the - * same mapping value. - * - *

A ConcurrentHashMapV8 can be used as scalable frequency map (a - * form of histogram or multiset) by using {@link LongAdder} values - * and initializing via {@link #computeIfAbsent}. For example, to add - * a count to a {@code ConcurrentHashMapV8 freqs}, you - * can use {@code freqs.computeIfAbsent(k -> new - * LongAdder()).increment();} - * - *

This class and its views and iterators implement all of the - * optional methods of the {@link Map} and {@link Iterator} - * interfaces. - * - *

Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow {@code null} to be used as a key or value. - * - *

ConcurrentHashMapV8s support parallel operations using the {@link - * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts - * are available in class {@link ForkJoinTasks}). These operations are - * designed to be safely, and often sensibly, applied even with maps - * that are being concurrently updated by other threads; for example, - * when computing a snapshot summary of the values in a shared - * registry. There are three kinds of operation, each with four - * forms, accepting functions with Keys, Values, Entries, and (Key, - * Value) arguments and/or return values. (The first three forms are - * also available via the {@link #keySet()}, {@link #values()} and - * {@link #entrySet()} views). Because the elements of a - * ConcurrentHashMapV8 are not ordered in any particular way, and may be - * processed in different orders in different parallel executions, the - * correctness of supplied functions should not depend on any - * ordering, or on any other objects or values that may transiently - * change while computation is in progress; and except for forEach - * actions, should ideally be side-effect-free. - * - *

    - *
  • forEach: Perform a given action on each element. - * A variant form applies a given transformation on each element - * before performing the action.
  • - * - *
  • search: Return the first available non-null result of - * applying a given function on each element; skipping further - * search when a result is found.
  • - * - *
  • reduce: Accumulate each element. The supplied reduction - * function cannot rely on ordering (more formally, it should be - * both associative and commutative). There are five variants: - * - *
      - * - *
    • Plain reductions. (There is not a form of this method for - * (key, value) function arguments since there is no corresponding - * return type.)
    • - * - *
    • Mapped reductions that accumulate the results of a given - * function applied to each element.
    • - * - *
    • Reductions to scalar doubles, longs, and ints, using a - * given basis value.
    • - * - * - *
    - *
- * - *

The concurrency properties of bulk operations follow - * from those of ConcurrentHashMapV8: Any non-null result returned - * from {@code get(key)} and related access methods bears a - * happens-before relation with the associated insertion or - * update. The result of any bulk operation reflects the - * composition of these per-element relations (but is not - * necessarily atomic with respect to the map as a whole unless it - * is somehow known to be quiescent). Conversely, because keys - * and values in the map are never null, null serves as a reliable - * atomic indicator of the current lack of any result. To - * maintain this property, null serves as an implicit basis for - * all non-scalar reduction operations. For the double, long, and - * int versions, the basis should be one that, when combined with - * any other value, returns that other value (more formally, it - * should be the identity element for the reduction). Most common - * reductions have these properties; for example, computing a sum - * with basis 0 or a minimum with basis MAX_VALUE. - * - *

Search and transformation functions provided as arguments - * should similarly return null to indicate the lack of any result - * (in which case it is not used). In the case of mapped - * reductions, this also enables transformations to serve as - * filters, returning null (or, in the case of primitive - * specializations, the identity basis) if the element should not - * be combined. You can create compound transformations and - * filterings by composing them yourself under this "null means - * there is nothing there now" rule before using them in search or - * reduce operations. - * - *

Methods accepting and/or returning Entry arguments maintain - * key-value associations. They may be useful for example when - * finding the key for the greatest value. Note that "plain" Entry - * arguments can be supplied using {@code new - * AbstractMap.SimpleEntry(k,v)}. - * - *

Bulk operations may complete abruptly, throwing an - * exception encountered in the application of a supplied - * function. Bear in mind when handling such exceptions that other - * concurrently executing functions could also have thrown - * exceptions, or would have done so if the first exception had - * not occurred. - * - *

Parallel speedups for bulk operations compared to sequential - * processing are common but not guaranteed. Operations involving - * brief functions on small maps may execute more slowly than - * sequential loops if the underlying work to parallelize the - * computation is more expensive than the computation itself. - * Similarly, parallelization may not lead to much actual parallelism - * if all processors are busy performing unrelated tasks. - * - *

All arguments to all task methods must be non-null. - * - *

jsr166e note: During transition, this class - * uses nested functional interfaces with different names but the - * same forms as those expected for JDK8. - * - *

This class is a member of the - * - * Java Collections Framework. - * - * @since 1.5 - * @author Doug Lea - * @param the type of keys maintained by this map - * @param the type of mapped values - */ -public class ConcurrentHashMapV8 - implements ConcurrentMap, Serializable, ConcurrentHashMap { - private static final long serialVersionUID = 7249069246763182397L; - - /** - * A partitionable iterator. A Spliterator can be traversed - * directly, but can also be partitioned (before traversal) by - * creating another Spliterator that covers a non-overlapping - * portion of the elements, and so may be amenable to parallel - * execution. - * - *

This interface exports a subset of expected JDK8 - * functionality. - * - *

Sample usage: Here is one (of the several) ways to compute - * the sum of the values held in a map using the ForkJoin - * framework. As illustrated here, Spliterators are well suited to - * designs in which a task repeatedly splits off half its work - * into forked subtasks until small enough to process directly, - * and then joins these subtasks. Variants of this style can also - * be used in completion-based designs. - * - *

-     * {@code ConcurrentHashMapV8 m = ...
-     * // split as if have 8 * parallelism, for load balance
-     * int n = m.size();
-     * int p = aForkJoinPool.getParallelism() * 8;
-     * int split = (n < p)? n : p;
-     * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
-     * // ...
-     * static class SumValues extends RecursiveTask {
-     *   final Spliterator s;
-     *   final int split;             // split while > 1
-     *   final SumValues nextJoin;    // records forked subtasks to join
-     *   SumValues(Spliterator s, int depth, SumValues nextJoin) {
-     *     this.s = s; this.depth = depth; this.nextJoin = nextJoin;
-     *   }
-     *   public Long compute() {
-     *     long sum = 0;
-     *     SumValues subtasks = null; // fork subtasks
-     *     for (int s = split >>> 1; s > 0; s >>>= 1)
-     *       (subtasks = new SumValues(s.split(), s, subtasks)).fork();
-     *     while (s.hasNext())        // directly process remaining elements
-     *       sum += s.next();
-     *     for (SumValues t = subtasks; t != null; t = t.nextJoin)
-     *       sum += t.join();         // collect subtask results
-     *     return sum;
-     *   }
-     * }
-     * }
- */ - public static interface Spliterator extends Iterator { - /** - * Returns a Spliterator covering approximately half of the - * elements, guaranteed not to overlap with those subsequently - * returned by this Spliterator. After invoking this method, - * the current Spliterator will not produce any of - * the elements of the returned Spliterator, but the two - * Spliterators together will produce all of the elements that - * would have been produced by this Spliterator had this - * method not been called. The exact number of elements - * produced by the returned Spliterator is not guaranteed, and - * may be zero (i.e., with {@code hasNext()} reporting {@code - * false}) if this Spliterator cannot be further split. - * - * @return a Spliterator covering approximately half of the - * elements - * @throws IllegalStateException if this Spliterator has - * already commenced traversing elements - */ - Spliterator split(); - } - - - /* - * Overview: - * - * The primary design goal of this hash table is to maintain - * concurrent readability (typically method get(), but also - * iterators and related methods) while minimizing update - * contention. Secondary goals are to keep space consumption about - * the same or better than java.util.HashMap, and to support high - * initial insertion rates on an empty table by many threads. - * - * Each key-value mapping is held in a Node. Because Node fields - * can contain special values, they are defined using plain Object - * types. Similarly in turn, all internal methods that use them - * work off Object types. And similarly, so do the internal - * methods of auxiliary iterator and view classes. All public - * generic typed methods relay in/out of these internal methods, - * supplying null-checks and casts as needed. This also allows - * many of the public methods to be factored into a smaller number - * of internal methods (although sadly not so for the five - * variants of put-related operations). The validation-based - * approach explained below leads to a lot of code sprawl because - * retry-control precludes factoring into smaller methods. - * - * The table is lazily initialized to a power-of-two size upon the - * first insertion. Each bin in the table normally contains a - * list of Nodes (most often, the list has only zero or one Node). - * Table accesses require volatile/atomic reads, writes, and - * CASes. Because there is no other way to arrange this without - * adding further indirections, we use intrinsics - * (sun.misc.Unsafe) operations. The lists of nodes within bins - * are always accurately traversable under volatile reads, so long - * as lookups check hash code and non-nullness of value before - * checking key equality. - * - * We use the top two bits of Node hash fields for control - * purposes -- they are available anyway because of addressing - * constraints. As explained further below, these top bits are - * used as follows: - * 00 - Normal - * 01 - Locked - * 11 - Locked and may have a thread waiting for lock - * 10 - Node is a forwarding node - * - * The lower 30 bits of each Node's hash field contain a - * transformation of the key's hash code, except for forwarding - * nodes, for which the lower bits are zero (and so always have - * hash field == MOVED). - * - * Insertion (via put or its variants) of the first node in an - * empty bin is performed by just CASing it to the bin. This is - * by far the most common case for put operations under most - * key/hash distributions. Other update operations (insert, - * delete, and replace) require locks. We do not want to waste - * the space required to associate a distinct lock object with - * each bin, so instead use the first node of a bin list itself as - * a lock. Blocking support for these locks relies on the builtin - * "synchronized" monitors. However, we also need a tryLock - * construction, so we overlay these by using bits of the Node - * hash field for lock control (see above), and so normally use - * builtin monitors only for blocking and signalling using - * wait/notifyAll constructions. See Node.tryAwaitLock. - * - * Using the first node of a list as a lock does not by itself - * suffice though: When a node is locked, any update must first - * validate that it is still the first node after locking it, and - * retry if not. Because new nodes are always appended to lists, - * once a node is first in a bin, it remains first until deleted - * or the bin becomes invalidated (upon resizing). However, - * operations that only conditionally update may inspect nodes - * until the point of update. This is a converse of sorts to the - * lazy locking technique described by Herlihy & Shavit. - * - * The main disadvantage of per-bin locks is that other update - * operations on other nodes in a bin list protected by the same - * lock can stall, for example when user equals() or mapping - * functions take a long time. However, statistically, under - * random hash codes, this is not a common problem. Ideally, the - * frequency of nodes in bins follows a Poisson distribution - * (http://en.wikipedia.org/wiki/Poisson_distribution) with a - * parameter of about 0.5 on average, given the resizing threshold - * of 0.75, although with a large variance because of resizing - * granularity. Ignoring variance, the expected occurrences of - * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The - * first values are: - * - * 0: 0.60653066 - * 1: 0.30326533 - * 2: 0.07581633 - * 3: 0.01263606 - * 4: 0.00157952 - * 5: 0.00015795 - * 6: 0.00001316 - * 7: 0.00000094 - * 8: 0.00000006 - * more: less than 1 in ten million - * - * Lock contention probability for two threads accessing distinct - * elements is roughly 1 / (8 * #elements) under random hashes. - * - * Actual hash code distributions encountered in practice - * sometimes deviate significantly from uniform randomness. This - * includes the case when N > (1<<30), so some keys MUST collide. - * Similarly for dumb or hostile usages in which multiple keys are - * designed to have identical hash codes. Also, although we guard - * against the worst effects of this (see method spread), sets of - * hashes may differ only in bits that do not impact their bin - * index for a given power-of-two mask. So we use a secondary - * strategy that applies when the number of nodes in a bin exceeds - * a threshold, and at least one of the keys implements - * Comparable. These TreeBins use a balanced tree to hold nodes - * (a specialized form of red-black trees), bounding search time - * to O(log N). Each search step in a TreeBin is around twice as - * slow as in a regular list, but given that N cannot exceed - * (1<<64) (before running out of addresses) this bounds search - * steps, lock hold times, etc, to reasonable constants (roughly - * 100 nodes inspected per operation worst case) so long as keys - * are Comparable (which is very common -- String, Long, etc). - * TreeBin nodes (TreeNodes) also maintain the same "next" - * traversal pointers as regular nodes, so can be traversed in - * iterators in the same way. - * - * The table is resized when occupancy exceeds a percentage - * threshold (nominally, 0.75, but see below). Only a single - * thread performs the resize (using field "sizeCtl", to arrange - * exclusion), but the table otherwise remains usable for reads - * and updates. Resizing proceeds by transferring bins, one by - * one, from the table to the next table. Because we are using - * power-of-two expansion, the elements from each bin must either - * stay at same index, or move with a power of two offset. We - * eliminate unnecessary node creation by catching cases where old - * nodes can be reused because their next fields won't change. On - * average, only about one-sixth of them need cloning when a table - * doubles. The nodes they replace will be garbage collectable as - * soon as they are no longer referenced by any reader thread that - * may be in the midst of concurrently traversing table. Upon - * transfer, the old table bin contains only a special forwarding - * node (with hash field "MOVED") that contains the next table as - * its key. On encountering a forwarding node, access and update - * operations restart, using the new table. - * - * Each bin transfer requires its bin lock. However, unlike other - * cases, a transfer can skip a bin if it fails to acquire its - * lock, and revisit it later (unless it is a TreeBin). Method - * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that - * have been skipped because of failure to acquire a lock, and - * blocks only if none are available (i.e., only very rarely). - * The transfer operation must also ensure that all accessible - * bins in both the old and new table are usable by any traversal. - * When there are no lock acquisition failures, this is arranged - * simply by proceeding from the last bin (table.length - 1) up - * towards the first. Upon seeing a forwarding node, traversals - * (see class Iter) arrange to move to the new table - * without revisiting nodes. However, when any node is skipped - * during a transfer, all earlier table bins may have become - * visible, so are initialized with a reverse-forwarding node back - * to the old table until the new ones are established. (This - * sometimes requires transiently locking a forwarding node, which - * is possible under the above encoding.) These more expensive - * mechanics trigger only when necessary. - * - * The traversal scheme also applies to partial traversals of - * ranges of bins (via an alternate Traverser constructor) - * to support partitioned aggregate operations. Also, read-only - * operations give up if ever forwarded to a null table, which - * provides support for shutdown-style clearing, which is also not - * currently implemented. - * - * Lazy table initialization minimizes footprint until first use, - * and also avoids resizings when the first operation is from a - * putAll, constructor with map argument, or deserialization. - * These cases attempt to override the initial capacity settings, - * but harmlessly fail to take effect in cases of races. - * - * The element count is maintained using a LongAdder, which avoids - * contention on updates but can encounter cache thrashing if read - * too frequently during concurrent access. To avoid reading so - * often, resizing is attempted either when a bin lock is - * contended, or upon adding to a bin already holding two or more - * nodes (checked before adding in the xIfAbsent methods, after - * adding in others). Under uniform hash distributions, the - * probability of this occurring at threshold is around 13%, - * meaning that only about 1 in 8 puts check threshold (and after - * resizing, many fewer do so). But this approximation has high - * variance for small table sizes, so we check on any collision - * for sizes <= 64. The bulk putAll operation further reduces - * contention by only committing count updates upon these size - * checks. - * - * Maintaining API and serialization compatibility with previous - * versions of this class introduces several oddities. Mainly: We - * leave untouched but unused constructor arguments refering to - * concurrencyLevel. We accept a loadFactor constructor argument, - * but apply it only to initial table capacity (which is the only - * time that we can guarantee to honor it.) We also declare an - * unused "Segment" class that is instantiated in minimal form - * only when serializing. - */ - - /* ---------------- Constants -------------- */ - - /** - * The largest possible table capacity. This value must be - * exactly 1<<30 to stay within Java array allocation and indexing - * bounds for power of two table sizes, and is further required - * because the top two bits of 32bit hash fields are used for - * control purposes. - */ - private static final int MAXIMUM_CAPACITY = 1 << 30; - - /** - * The default initial table capacity. Must be a power of 2 - * (i.e., at least 1) and at most MAXIMUM_CAPACITY. - */ - private static final int DEFAULT_CAPACITY = 16; - - /** - * The largest possible (non-power of two) array size. - * Needed by toArray and related methods. - */ - static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - - /** - * The default concurrency level for this table. Unused but - * defined for compatibility with previous versions of this class. - */ - private static final int DEFAULT_CONCURRENCY_LEVEL = 16; - - /** - * The load factor for this table. Overrides of this value in - * constructors affect only the initial table capacity. The - * actual floating point value isn't normally used -- it is - * simpler to use expressions such as {@code n - (n >>> 2)} for - * the associated resizing threshold. - */ - private static final float LOAD_FACTOR = 0.75f; - - /** - * The buffer size for skipped bins during transfers. The - * value is arbitrary but should be large enough to avoid - * most locking stalls during resizes. - */ - private static final int TRANSFER_BUFFER_SIZE = 32; - - /** - * The bin count threshold for using a tree rather than list for a - * bin. The value reflects the approximate break-even point for - * using tree-based operations. - * Note that Doug's version defaults to 8, but when dealing with - * Ruby objects it is actually beneficial to avoid TreeNodes - * as long as possible as it usually means going into Ruby land. - */ - private static final int TREE_THRESHOLD = 16; - - /* - * Encodings for special uses of Node hash fields. See above for - * explanation. - */ - static final int MOVED = 0x80000000; // hash field for forwarding nodes - static final int LOCKED = 0x40000000; // set/tested only as a bit - static final int WAITING = 0xc0000000; // both bits set/tested together - static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash - - /* ---------------- Fields -------------- */ - - /** - * The array of bins. Lazily initialized upon first insertion. - * Size is always a power of two. Accessed directly by iterators. - */ - transient volatile Node[] table; - - /** - * The counter maintaining number of elements. - */ - private transient final LongAdder counter; - - /** - * Table initialization and resizing control. When negative, the - * table is being initialized or resized. Otherwise, when table is - * null, holds the initial table size to use upon creation, or 0 - * for default. After initialization, holds the next element count - * value upon which to resize the table. - */ - private transient volatile int sizeCtl; - - // views - private transient KeySetView keySet; - private transient ValuesView values; - private transient EntrySetView entrySet; - - /** For serialization compatibility. Null unless serialized; see below */ - private Segment[] segments; - - /* ---------------- Table element access -------------- */ - - /* - * Volatile access methods are used for table elements as well as - * elements of in-progress next table while resizing. Uses are - * null checked by callers, and implicitly bounds-checked, relying - * on the invariants that tab arrays have non-zero size, and all - * indices are masked with (tab.length - 1) which is never - * negative and always less than length. Note that, to be correct - * wrt arbitrary concurrency errors by users, bounds checks must - * operate on local variables, which accounts for some odd-looking - * inline assignments below. - */ - - static final Node tabAt(Node[] tab, int i) { // used by Iter - return (Node)UNSAFE.getObjectVolatile(tab, ((long)i< 1 ? 64 : 1; - - /** - * Spins a while if LOCKED bit set and this node is the first - * of its bin, and then sets WAITING bits on hash field and - * blocks (once) if they are still set. It is OK for this - * method to return even if lock is not available upon exit, - * which enables these simple single-wait mechanics. - * - * The corresponding signalling operation is performed within - * callers: Upon detecting that WAITING has been set when - * unlocking lock (via a failed CAS from non-waiting LOCKED - * state), unlockers acquire the sync lock and perform a - * notifyAll. - * - * The initial sanity check on tab and bounds is not currently - * necessary in the only usages of this method, but enables - * use in other future contexts. - */ - final void tryAwaitLock(Node[] tab, int i) { - if (tab != null && i >= 0 && i < tab.length) { // sanity check - int r = ThreadLocalRandom.current().nextInt(); // randomize spins - int spins = MAX_SPINS, h; - while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) { - if (spins >= 0) { - r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift - if (r >= 0 && --spins == 0) - Thread.yield(); // yield before block - } - else if (casHash(h, h | WAITING)) { - synchronized (this) { - if (tabAt(tab, i) == this && - (hash & WAITING) == WAITING) { - try { - wait(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - } - else - notifyAll(); // possibly won race vs signaller - } - break; - } - } - } - } - - // Unsafe mechanics for casHash - private static final sun.misc.Unsafe UNSAFE; - private static final long hashOffset; - - static { - try { - UNSAFE = getUnsafe(); - Class k = Node.class; - hashOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("hash")); - } catch (Exception e) { - throw new Error(e); - } - } - } - - /* ---------------- TreeBins -------------- */ - - /** - * Nodes for use in TreeBins - */ - static final class TreeNode extends Node { - TreeNode parent; // red-black tree links - TreeNode left; - TreeNode right; - TreeNode prev; // needed to unlink next upon deletion - boolean red; - - TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) { - super(hash, key, val, next); - this.parent = parent; - } - } - - /** - * A specialized form of red-black tree for use in bins - * whose size exceeds a threshold. - * - * TreeBins use a special form of comparison for search and - * related operations (which is the main reason we cannot use - * existing collections such as TreeMaps). TreeBins contain - * Comparable elements, but may contain others, as well as - * elements that are Comparable but not necessarily Comparable - * for the same T, so we cannot invoke compareTo among them. To - * handle this, the tree is ordered primarily by hash value, then - * by getClass().getName() order, and then by Comparator order - * among elements of the same class. On lookup at a node, if - * elements are not comparable or compare as 0, both left and - * right children may need to be searched in the case of tied hash - * values. (This corresponds to the full list search that would be - * necessary if all elements were non-Comparable and had tied - * hashes.) The red-black balancing code is updated from - * pre-jdk-collections - * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) - * based in turn on Cormen, Leiserson, and Rivest "Introduction to - * Algorithms" (CLR). - * - * TreeBins also maintain a separate locking discipline than - * regular bins. Because they are forwarded via special MOVED - * nodes at bin heads (which can never change once established), - * we cannot use those nodes as locks. Instead, TreeBin - * extends AbstractQueuedSynchronizer to support a simple form of - * read-write lock. For update operations and table validation, - * the exclusive form of lock behaves in the same way as bin-head - * locks. However, lookups use shared read-lock mechanics to allow - * multiple readers in the absence of writers. Additionally, - * these lookups do not ever block: While the lock is not - * available, they proceed along the slow traversal path (via - * next-pointers) until the lock becomes available or the list is - * exhausted, whichever comes first. (These cases are not fast, - * but maximize aggregate expected throughput.) The AQS mechanics - * for doing this are straightforward. The lock state is held as - * AQS getState(). Read counts are negative; the write count (1) - * is positive. There are no signalling preferences among readers - * and writers. Since we don't need to export full Lock API, we - * just override the minimal AQS methods and use them directly. - */ - static final class TreeBin extends AbstractQueuedSynchronizer { - private static final long serialVersionUID = 2249069246763182397L; - transient TreeNode root; // root of tree - transient TreeNode first; // head of next-pointer list - - /* AQS overrides */ - public final boolean isHeldExclusively() { return getState() > 0; } - public final boolean tryAcquire(int ignore) { - if (compareAndSetState(0, 1)) { - setExclusiveOwnerThread(Thread.currentThread()); - return true; - } - return false; - } - public final boolean tryRelease(int ignore) { - setExclusiveOwnerThread(null); - setState(0); - return true; - } - public final int tryAcquireShared(int ignore) { - for (int c;;) { - if ((c = getState()) > 0) - return -1; - if (compareAndSetState(c, c -1)) - return 1; - } - } - public final boolean tryReleaseShared(int ignore) { - int c; - do {} while (!compareAndSetState(c = getState(), c + 1)); - return c == -1; - } - - /** From CLR */ - private void rotateLeft(TreeNode p) { - if (p != null) { - TreeNode r = p.right, pp, rl; - if ((rl = p.right = r.left) != null) - rl.parent = p; - if ((pp = r.parent = p.parent) == null) - root = r; - else if (pp.left == p) - pp.left = r; - else - pp.right = r; - r.left = p; - p.parent = r; - } - } - - /** From CLR */ - private void rotateRight(TreeNode p) { - if (p != null) { - TreeNode l = p.left, pp, lr; - if ((lr = p.left = l.right) != null) - lr.parent = p; - if ((pp = l.parent = p.parent) == null) - root = l; - else if (pp.right == p) - pp.right = l; - else - pp.left = l; - l.right = p; - p.parent = l; - } - } - - @SuppressWarnings("unchecked") final TreeNode getTreeNode - (int h, Object k, TreeNode p) { - return getTreeNode(h, (RubyObject)k, p); - } - - /** - * Returns the TreeNode (or null if not found) for the given key - * starting at given root. - */ - @SuppressWarnings("unchecked") final TreeNode getTreeNode - (int h, RubyObject k, TreeNode p) { - RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>"); - while (p != null) { - int dir, ph; RubyObject pk; RubyClass pc; - if ((ph = p.hash) == h) { - if ((pk = (RubyObject)p.key) == k || k.equals(pk)) - return p; - if (c != (pc = (RubyClass)pk.getMetaClass()) || - kNotComparable || - (dir = rubyCompare(k, pk)) == 0) { - dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); - if (dir == 0) { // if still stuck, need to check both sides - TreeNode r = null, pl, pr; - // try to recurse on the right - if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) - return r; - // try to continue iterating on the left side - else if ((pl = p.left) != null && h <= pl.hash) - dir = -1; - else // no matching node found - return null; - } - } - } - else - dir = (h < ph) ? -1 : 1; - p = (dir > 0) ? p.right : p.left; - } - return null; - } - - int rubyCompare(RubyObject l, RubyObject r) { - ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext(); - IRubyObject result; - try { - result = l.callMethod(context, "<=>", r); - } catch (RaiseException e) { - // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys - if (context.runtime.getNoMethodError().isInstance(e.getException())) { - return 0; - } - throw e; - } - - return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger()); - } - - /** - * Wrapper for getTreeNode used by CHM.get. Tries to obtain - * read-lock to call getTreeNode, but during failure to get - * lock, searches along next links. - */ - final Object getValue(int h, Object k) { - Node r = null; - int c = getState(); // Must read lock state first - for (Node e = first; e != null; e = e.next) { - if (c <= 0 && compareAndSetState(c, c - 1)) { - try { - r = getTreeNode(h, k, root); - } finally { - releaseShared(0); - } - break; - } - else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) { - r = e; - break; - } - else - c = getState(); - } - return r == null ? null : r.val; - } - - @SuppressWarnings("unchecked") final TreeNode putTreeNode - (int h, Object k, Object v) { - return putTreeNode(h, (RubyObject)k, v); - } - - /** - * Finds or adds a node. - * @return null if added - */ - @SuppressWarnings("unchecked") final TreeNode putTreeNode - (int h, RubyObject k, Object v) { - RubyClass c = k.getMetaClass(); - boolean kNotComparable = !k.respondsTo("<=>"); - TreeNode pp = root, p = null; - int dir = 0; - while (pp != null) { // find existing node or leaf to insert at - int ph; RubyObject pk; RubyClass pc; - p = pp; - if ((ph = p.hash) == h) { - if ((pk = (RubyObject)p.key) == k || k.equals(pk)) - return p; - if (c != (pc = pk.getMetaClass()) || - kNotComparable || - (dir = rubyCompare(k, pk)) == 0) { - dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); - if (dir == 0) { // if still stuck, need to check both sides - TreeNode r = null, pr; - // try to recurse on the right - if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) - return r; - else // continue descending down the left subtree - dir = -1; - } - } - } - else - dir = (h < ph) ? -1 : 1; - pp = (dir > 0) ? p.right : p.left; - } - - TreeNode f = first; - TreeNode x = first = new TreeNode(h, (Object)k, v, f, p); - if (p == null) - root = x; - else { // attach and rebalance; adapted from CLR - TreeNode xp, xpp; - if (f != null) - f.prev = x; - if (dir <= 0) - p.left = x; - else - p.right = x; - x.red = true; - while (x != null && (xp = x.parent) != null && xp.red && - (xpp = xp.parent) != null) { - TreeNode xppl = xpp.left; - if (xp == xppl) { - TreeNode y = xpp.right; - if (y != null && y.red) { - y.red = false; - xp.red = false; - xpp.red = true; - x = xpp; - } - else { - if (x == xp.right) { - rotateLeft(x = xp); - xpp = (xp = x.parent) == null ? null : xp.parent; - } - if (xp != null) { - xp.red = false; - if (xpp != null) { - xpp.red = true; - rotateRight(xpp); - } - } - } - } - else { - TreeNode y = xppl; - if (y != null && y.red) { - y.red = false; - xp.red = false; - xpp.red = true; - x = xpp; - } - else { - if (x == xp.left) { - rotateRight(x = xp); - xpp = (xp = x.parent) == null ? null : xp.parent; - } - if (xp != null) { - xp.red = false; - if (xpp != null) { - xpp.red = true; - rotateLeft(xpp); - } - } - } - } - } - TreeNode r = root; - if (r != null && r.red) - r.red = false; - } - return null; - } - - /** - * Removes the given node, that must be present before this - * call. This is messier than typical red-black deletion code - * because we cannot swap the contents of an interior node - * with a leaf successor that is pinned by "next" pointers - * that are accessible independently of lock. So instead we - * swap the tree linkages. - */ - final void deleteTreeNode(TreeNode p) { - TreeNode next = (TreeNode)p.next; // unlink traversal pointers - TreeNode pred = p.prev; - if (pred == null) - first = next; - else - pred.next = next; - if (next != null) - next.prev = pred; - TreeNode replacement; - TreeNode pl = p.left; - TreeNode pr = p.right; - if (pl != null && pr != null) { - TreeNode s = pr, sl; - while ((sl = s.left) != null) // find successor - s = sl; - boolean c = s.red; s.red = p.red; p.red = c; // swap colors - TreeNode sr = s.right; - TreeNode pp = p.parent; - if (s == pr) { // p was s's direct parent - p.parent = s; - s.right = p; - } - else { - TreeNode sp = s.parent; - if ((p.parent = sp) != null) { - if (s == sp.left) - sp.left = p; - else - sp.right = p; - } - if ((s.right = pr) != null) - pr.parent = s; - } - p.left = null; - if ((p.right = sr) != null) - sr.parent = p; - if ((s.left = pl) != null) - pl.parent = s; - if ((s.parent = pp) == null) - root = s; - else if (p == pp.left) - pp.left = s; - else - pp.right = s; - replacement = sr; - } - else - replacement = (pl != null) ? pl : pr; - TreeNode pp = p.parent; - if (replacement == null) { - if (pp == null) { - root = null; - return; - } - replacement = p; - } - else { - replacement.parent = pp; - if (pp == null) - root = replacement; - else if (p == pp.left) - pp.left = replacement; - else - pp.right = replacement; - p.left = p.right = p.parent = null; - } - if (!p.red) { // rebalance, from CLR - TreeNode x = replacement; - while (x != null) { - TreeNode xp, xpl; - if (x.red || (xp = x.parent) == null) { - x.red = false; - break; - } - if (x == (xpl = xp.left)) { - TreeNode sib = xp.right; - if (sib != null && sib.red) { - sib.red = false; - xp.red = true; - rotateLeft(xp); - sib = (xp = x.parent) == null ? null : xp.right; - } - if (sib == null) - x = xp; - else { - TreeNode sl = sib.left, sr = sib.right; - if ((sr == null || !sr.red) && - (sl == null || !sl.red)) { - sib.red = true; - x = xp; - } - else { - if (sr == null || !sr.red) { - if (sl != null) - sl.red = false; - sib.red = true; - rotateRight(sib); - sib = (xp = x.parent) == null ? null : xp.right; - } - if (sib != null) { - sib.red = (xp == null) ? false : xp.red; - if ((sr = sib.right) != null) - sr.red = false; - } - if (xp != null) { - xp.red = false; - rotateLeft(xp); - } - x = root; - } - } - } - else { // symmetric - TreeNode sib = xpl; - if (sib != null && sib.red) { - sib.red = false; - xp.red = true; - rotateRight(xp); - sib = (xp = x.parent) == null ? null : xp.left; - } - if (sib == null) - x = xp; - else { - TreeNode sl = sib.left, sr = sib.right; - if ((sl == null || !sl.red) && - (sr == null || !sr.red)) { - sib.red = true; - x = xp; - } - else { - if (sl == null || !sl.red) { - if (sr != null) - sr.red = false; - sib.red = true; - rotateLeft(sib); - sib = (xp = x.parent) == null ? null : xp.left; - } - if (sib != null) { - sib.red = (xp == null) ? false : xp.red; - if ((sl = sib.left) != null) - sl.red = false; - } - if (xp != null) { - xp.red = false; - rotateRight(xp); - } - x = root; - } - } - } - } - } - if (p == replacement && (pp = p.parent) != null) { - if (p == pp.left) // detach pointers - pp.left = null; - else if (p == pp.right) - pp.right = null; - p.parent = null; - } - } - } - - /* ---------------- Collision reduction methods -------------- */ - - /** - * Spreads higher bits to lower, and also forces top 2 bits to 0. - * Because the table uses power-of-two masking, sets of hashes - * that vary only in bits above the current mask will always - * collide. (Among known examples are sets of Float keys holding - * consecutive whole numbers in small tables.) To counter this, - * we apply a transform that spreads the impact of higher bits - * downward. There is a tradeoff between speed, utility, and - * quality of bit-spreading. Because many common sets of hashes - * are already reasonably distributed across bits (so don't benefit - * from spreading), and because we use trees to handle large sets - * of collisions in bins, we don't need excessively high quality. - */ - private static final int spread(int h) { - h ^= (h >>> 18) ^ (h >>> 12); - return (h ^ (h >>> 10)) & HASH_BITS; - } - - /** - * Replaces a list bin with a tree bin. Call only when locked. - * Fails to replace if the given key is non-comparable or table - * is, or needs, resizing. - */ - private final void replaceWithTreeBin(Node[] tab, int index, Object key) { - if ((key instanceof Comparable) && - (tab.length >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) { - TreeBin t = new TreeBin(); - for (Node e = tabAt(tab, index); e != null; e = e.next) - t.putTreeNode(e.hash & HASH_BITS, e.key, e.val); - setTabAt(tab, index, new Node(MOVED, t, null, null)); - } - } - - /* ---------------- Internal access and update methods -------------- */ - - /** Implementation for get and containsKey */ - private final Object internalGet(Object k) { - int h = spread(k.hashCode()); - retry: for (Node[] tab = table; tab != null;) { - Node e, p; Object ek, ev; int eh; // locals to read fields once - for (e = tabAt(tab, (tab.length - 1) & h); e != null; e = e.next) { - if ((eh = e.hash) == MOVED) { - if ((ek = e.key) instanceof TreeBin) // search TreeBin - return ((TreeBin)ek).getValue(h, k); - else { // restart with new table - tab = (Node[])ek; - continue retry; - } - } - else if ((eh & HASH_BITS) == h && (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) - return ev; - } - break; - } - return null; - } - - /** - * Implementation for the four public remove/replace methods: - * Replaces node value with v, conditional upon match of cv if - * non-null. If resulting value is null, delete. - */ - private final Object internalReplace(Object k, Object v, Object cv) { - int h = spread(k.hashCode()); - Object oldVal = null; - for (Node[] tab = table;;) { - Node f; int i, fh; Object fk; - if (tab == null || - (f = tabAt(tab, i = (tab.length - 1) & h)) == null) - break; - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean validated = false; - boolean deleted = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - validated = true; - TreeNode p = t.getTreeNode(h, k, t.root); - if (p != null) { - Object pv = p.val; - if (cv == null || cv == pv || cv.equals(pv)) { - oldVal = pv; - if ((p.val = v) == null) { - deleted = true; - t.deleteTreeNode(p); - } - } - } - } - } finally { - t.release(0); - } - if (validated) { - if (deleted) - counter.add(-1L); - break; - } - } - else - tab = (Node[])fk; - } - else if ((fh & HASH_BITS) != h && f.next == null) // precheck - break; // rules out possible existence - else if ((fh & LOCKED) != 0) { - checkForResize(); // try resizing if can't get lock - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - boolean validated = false; - boolean deleted = false; - try { - if (tabAt(tab, i) == f) { - validated = true; - for (Node e = f, pred = null;;) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - ((ev = e.val) != null) && - ((ek = e.key) == k || k.equals(ek))) { - if (cv == null || cv == ev || cv.equals(ev)) { - oldVal = ev; - if ((e.val = v) == null) { - deleted = true; - Node en = e.next; - if (pred != null) - pred.next = en; - else - setTabAt(tab, i, en); - } - } - break; - } - pred = e; - if ((e = e.next) == null) - break; - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (validated) { - if (deleted) - counter.add(-1L); - break; - } - } - } - return oldVal; - } - - /* - * Internal versions of the six insertion methods, each a - * little more complicated than the last. All have - * the same basic structure as the first (internalPut): - * 1. If table uninitialized, create - * 2. If bin empty, try to CAS new node - * 3. If bin stale, use new table - * 4. if bin converted to TreeBin, validate and relay to TreeBin methods - * 5. Lock and validate; if valid, scan and add or update - * - * The others interweave other checks and/or alternative actions: - * * Plain put checks for and performs resize after insertion. - * * putIfAbsent prescans for mapping without lock (and fails to add - * if present), which also makes pre-emptive resize checks worthwhile. - * * computeIfAbsent extends form used in putIfAbsent with additional - * mechanics to deal with, calls, potential exceptions and null - * returns from function call. - * * compute uses the same function-call mechanics, but without - * the prescans - * * merge acts as putIfAbsent in the absent case, but invokes the - * update function if present - * * putAll attempts to pre-allocate enough table space - * and more lazily performs count updates and checks. - * - * Someday when details settle down a bit more, it might be worth - * some factoring to reduce sprawl. - */ - - /** Implementation for put */ - private final Object internalPut(Object k, Object v) { - int h = spread(k.hashCode()); - int count = 0; - for (Node[] tab = table;;) { - int i; Node f; int fh; Object fk; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { - if (casTabAt(tab, i, null, new Node(h, k, v, null))) - break; // no lock when adding to empty bin - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - Object oldVal = null; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 2; - TreeNode p = t.putTreeNode(h, k, v); - if (p != null) { - oldVal = p.val; - p.val = v; - } - } - } finally { - t.release(0); - } - if (count != 0) { - if (oldVal != null) - return oldVal; - break; - } - } - else - tab = (Node[])fk; - } - else if ((fh & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - Object oldVal = null; - try { // needed in case equals() throws - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - oldVal = ev; - e.val = v; - break; - } - Node last = e; - if ((e = e.next) == null) { - last.next = new Node(h, k, v, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { // unlock and signal if needed - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (oldVal != null) - return oldVal; - if (tab.length <= 64) - count = 2; - break; - } - } - } - counter.add(1L); - if (count > 1) - checkForResize(); - return null; - } - - /** Implementation for putIfAbsent */ - private final Object internalPutIfAbsent(Object k, Object v) { - int h = spread(k.hashCode()); - int count = 0; - for (Node[] tab = table;;) { - int i; Node f; int fh; Object fk, fv; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { - if (casTabAt(tab, i, null, new Node(h, k, v, null))) - break; - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - Object oldVal = null; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 2; - TreeNode p = t.putTreeNode(h, k, v); - if (p != null) - oldVal = p.val; - } - } finally { - t.release(0); - } - if (count != 0) { - if (oldVal != null) - return oldVal; - break; - } - } - else - tab = (Node[])fk; - } - else if ((fh & HASH_BITS) == h && (fv = f.val) != null && - ((fk = f.key) == k || k.equals(fk))) - return fv; - else { - Node g = f.next; - if (g != null) { // at least 2 nodes -- search and maybe resize - for (Node e = g;;) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) - return ev; - if ((e = e.next) == null) { - checkForResize(); - break; - } - } - } - if (((fh = f.hash) & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { - Object oldVal = null; - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - oldVal = ev; - break; - } - Node last = e; - if ((e = e.next) == null) { - last.next = new Node(h, k, v, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (oldVal != null) - return oldVal; - if (tab.length <= 64) - count = 2; - break; - } - } - } - } - counter.add(1L); - if (count > 1) - checkForResize(); - return null; - } - - /** Implementation for computeIfAbsent */ - private final Object internalComputeIfAbsent(K k, - Fun mf) { - int h = spread(k.hashCode()); - Object val = null; - int count = 0; - for (Node[] tab = table;;) { - Node f; int i, fh; Object fk, fv; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { - Node node = new Node(fh = h | LOCKED, k, null, null); - if (casTabAt(tab, i, null, node)) { - count = 1; - try { - if ((val = mf.apply(k)) != null) - node.val = val; - } finally { - if (val == null) - setTabAt(tab, i, null); - if (!node.casHash(fh, h)) { - node.hash = h; - synchronized (node) { node.notifyAll(); }; - } - } - } - if (count != 0) - break; - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean added = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 1; - TreeNode p = t.getTreeNode(h, k, t.root); - if (p != null) - val = p.val; - else if ((val = mf.apply(k)) != null) { - added = true; - count = 2; - t.putTreeNode(h, k, val); - } - } - } finally { - t.release(0); - } - if (count != 0) { - if (!added) - return val; - break; - } - } - else - tab = (Node[])fk; - } - else if ((fh & HASH_BITS) == h && (fv = f.val) != null && - ((fk = f.key) == k || k.equals(fk))) - return fv; - else { - Node g = f.next; - if (g != null) { - for (Node e = g;;) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) - return ev; - if ((e = e.next) == null) { - checkForResize(); - break; - } - } - } - if (((fh = f.hash) & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { - boolean added = false; - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - val = ev; - break; - } - Node last = e; - if ((e = e.next) == null) { - if ((val = mf.apply(k)) != null) { - added = true; - last.next = new Node(h, k, val, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - } - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (!added) - return val; - if (tab.length <= 64) - count = 2; - break; - } - } - } - } - if (val != null) { - counter.add(1L); - if (count > 1) - checkForResize(); - } - return val; - } - - /** Implementation for compute */ - @SuppressWarnings("unchecked") private final Object internalCompute - (K k, boolean onlyIfPresent, BiFun mf) { - int h = spread(k.hashCode()); - Object val = null; - int delta = 0; - int count = 0; - for (Node[] tab = table;;) { - Node f; int i, fh; Object fk; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { - if (onlyIfPresent) - break; - Node node = new Node(fh = h | LOCKED, k, null, null); - if (casTabAt(tab, i, null, node)) { - try { - count = 1; - if ((val = mf.apply(k, null)) != null) { - node.val = val; - delta = 1; - } - } finally { - if (delta == 0) - setTabAt(tab, i, null); - if (!node.casHash(fh, h)) { - node.hash = h; - synchronized (node) { node.notifyAll(); }; - } - } - } - if (count != 0) - break; - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 1; - TreeNode p = t.getTreeNode(h, k, t.root); - Object pv; - if (p == null) { - if (onlyIfPresent) - break; - pv = null; - } else - pv = p.val; - if ((val = mf.apply(k, (V)pv)) != null) { - if (p != null) - p.val = val; - else { - count = 2; - delta = 1; - t.putTreeNode(h, k, val); - } - } - else if (p != null) { - delta = -1; - t.deleteTreeNode(p); - } - } - } finally { - t.release(0); - } - if (count != 0) - break; - } - else - tab = (Node[])fk; - } - else if ((fh & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f, pred = null;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - val = mf.apply(k, (V)ev); - if (val != null) - e.val = val; - else { - delta = -1; - Node en = e.next; - if (pred != null) - pred.next = en; - else - setTabAt(tab, i, en); - } - break; - } - pred = e; - if ((e = e.next) == null) { - if (!onlyIfPresent && (val = mf.apply(k, null)) != null) { - pred.next = new Node(h, k, val, null); - delta = 1; - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - } - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (tab.length <= 64) - count = 2; - break; - } - } - } - if (delta != 0) { - counter.add((long)delta); - if (count > 1) - checkForResize(); - } - return val; - } - - /** Implementation for merge */ - @SuppressWarnings("unchecked") private final Object internalMerge - (K k, V v, BiFun mf) { - int h = spread(k.hashCode()); - Object val = null; - int delta = 0; - int count = 0; - for (Node[] tab = table;;) { - int i; Node f; int fh; Object fk, fv; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null) { - if (casTabAt(tab, i, null, new Node(h, k, v, null))) { - delta = 1; - val = v; - break; - } - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 1; - TreeNode p = t.getTreeNode(h, k, t.root); - val = (p == null) ? v : mf.apply((V)p.val, v); - if (val != null) { - if (p != null) - p.val = val; - else { - count = 2; - delta = 1; - t.putTreeNode(h, k, val); - } - } - else if (p != null) { - delta = -1; - t.deleteTreeNode(p); - } - } - } finally { - t.release(0); - } - if (count != 0) - break; - } - else - tab = (Node[])fk; - } - else if ((fh & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f, pred = null;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - val = mf.apply((V)ev, v); - if (val != null) - e.val = val; - else { - delta = -1; - Node en = e.next; - if (pred != null) - pred.next = en; - else - setTabAt(tab, i, en); - } - break; - } - pred = e; - if ((e = e.next) == null) { - val = v; - pred.next = new Node(h, k, val, null); - delta = 1; - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (tab.length <= 64) - count = 2; - break; - } - } - } - if (delta != 0) { - counter.add((long)delta); - if (count > 1) - checkForResize(); - } - return val; - } - - /** Implementation for putAll */ - private final void internalPutAll(Map m) { - tryPresize(m.size()); - long delta = 0L; // number of uncommitted additions - boolean npe = false; // to throw exception on exit for nulls - try { // to clean up counts on other exceptions - for (Map.Entry entry : m.entrySet()) { - Object k, v; - if (entry == null || (k = entry.getKey()) == null || - (v = entry.getValue()) == null) { - npe = true; - break; - } - int h = spread(k.hashCode()); - for (Node[] tab = table;;) { - int i; Node f; int fh; Object fk; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length - 1) & h)) == null){ - if (casTabAt(tab, i, null, new Node(h, k, v, null))) { - ++delta; - break; - } - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean validated = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - validated = true; - TreeNode p = t.getTreeNode(h, k, t.root); - if (p != null) - p.val = v; - else { - t.putTreeNode(h, k, v); - ++delta; - } - } - } finally { - t.release(0); - } - if (validated) - break; - } - else - tab = (Node[])fk; - } - else if ((fh & LOCKED) != 0) { - counter.add(delta); - delta = 0L; - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - int count = 0; - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - e.val = v; - break; - } - Node last = e; - if ((e = e.next) == null) { - ++delta; - last.next = new Node(h, k, v, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (count > 1) { - counter.add(delta); - delta = 0L; - checkForResize(); - } - break; - } - } - } - } - } finally { - if (delta != 0) - counter.add(delta); - } - if (npe) - throw new NullPointerException(); - } - - /* ---------------- Table Initialization and Resizing -------------- */ - - /** - * Returns a power of two table size for the given desired capacity. - * See Hackers Delight, sec 3.2 - */ - private static final int tableSizeFor(int c) { - int n = c - 1; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; - } - - /** - * Initializes table, using the size recorded in sizeCtl. - */ - private final Node[] initTable() { - Node[] tab; int sc; - while ((tab = table) == null) { - if ((sc = sizeCtl) < 0) - Thread.yield(); // lost initialization race; just spin - else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { - try { - if ((tab = table) == null) { - int n = (sc > 0) ? sc : DEFAULT_CAPACITY; - tab = table = new Node[n]; - sc = n - (n >>> 2); - } - } finally { - sizeCtl = sc; - } - break; - } - } - return tab; - } - - /** - * If table is too small and not already resizing, creates next - * table and transfers bins. Rechecks occupancy after a transfer - * to see if another resize is already needed because resizings - * are lagging additions. - */ - private final void checkForResize() { - Node[] tab; int n, sc; - while ((tab = table) != null && - (n = tab.length) < MAXIMUM_CAPACITY && - (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc && - UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { - try { - if (tab == table) { - table = rebuild(tab); - sc = (n << 1) - (n >>> 1); - } - } finally { - sizeCtl = sc; - } - } - } - - /** - * Tries to presize table to accommodate the given number of elements. - * - * @param size number of elements (doesn't need to be perfectly accurate) - */ - private final void tryPresize(int size) { - int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : - tableSizeFor(size + (size >>> 1) + 1); - int sc; - while ((sc = sizeCtl) >= 0) { - Node[] tab = table; int n; - if (tab == null || (n = tab.length) == 0) { - n = (sc > c) ? sc : c; - if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { - try { - if (table == tab) { - table = new Node[n]; - sc = n - (n >>> 2); - } - } finally { - sizeCtl = sc; - } - } - } - else if (c <= sc || n >= MAXIMUM_CAPACITY) - break; - else if (UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { - try { - if (table == tab) { - table = rebuild(tab); - sc = (n << 1) - (n >>> 1); - } - } finally { - sizeCtl = sc; - } - } - } - } - - /* - * Moves and/or copies the nodes in each bin to new table. See - * above for explanation. - * - * @return the new table - */ - private static final Node[] rebuild(Node[] tab) { - int n = tab.length; - Node[] nextTab = new Node[n << 1]; - Node fwd = new Node(MOVED, nextTab, null, null); - int[] buffer = null; // holds bins to revisit; null until needed - Node rev = null; // reverse forwarder; null until needed - int nbuffered = 0; // the number of bins in buffer list - int bufferIndex = 0; // buffer index of current buffered bin - int bin = n - 1; // current non-buffered bin or -1 if none - - for (int i = bin;;) { // start upwards sweep - int fh; Node f; - if ((f = tabAt(tab, i)) == null) { - if (bin >= 0) { // Unbuffered; no lock needed (or available) - if (!casTabAt(tab, i, f, fwd)) - continue; - } - else { // transiently use a locked forwarding node - Node g = new Node(MOVED|LOCKED, nextTab, null, null); - if (!casTabAt(tab, i, f, g)) - continue; - setTabAt(nextTab, i, null); - setTabAt(nextTab, i + n, null); - setTabAt(tab, i, fwd); - if (!g.casHash(MOVED|LOCKED, MOVED)) { - g.hash = MOVED; - synchronized (g) { g.notifyAll(); } - } - } - } - else if ((fh = f.hash) == MOVED) { - Object fk = f.key; - if (fk instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean validated = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - validated = true; - splitTreeBin(nextTab, i, t); - setTabAt(tab, i, fwd); - } - } finally { - t.release(0); - } - if (!validated) - continue; - } - } - else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) { - boolean validated = false; - try { // split to lo and hi lists; copying as needed - if (tabAt(tab, i) == f) { - validated = true; - splitBin(nextTab, i, f); - setTabAt(tab, i, fwd); - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (!validated) - continue; - } - else { - if (buffer == null) // initialize buffer for revisits - buffer = new int[TRANSFER_BUFFER_SIZE]; - if (bin < 0 && bufferIndex > 0) { - int j = buffer[--bufferIndex]; - buffer[bufferIndex] = i; - i = j; // swap with another bin - continue; - } - if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) { - f.tryAwaitLock(tab, i); - continue; // no other options -- block - } - if (rev == null) // initialize reverse-forwarder - rev = new Node(MOVED, tab, null, null); - if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0) - continue; // recheck before adding to list - buffer[nbuffered++] = i; - setTabAt(nextTab, i, rev); // install place-holders - setTabAt(nextTab, i + n, rev); - } - - if (bin > 0) - i = --bin; - else if (buffer != null && nbuffered > 0) { - bin = -1; - i = buffer[bufferIndex = --nbuffered]; - } - else - return nextTab; - } - } - - /** - * Splits a normal bin with list headed by e into lo and hi parts; - * installs in given table. - */ - private static void splitBin(Node[] nextTab, int i, Node e) { - int bit = nextTab.length >>> 1; // bit to split on - int runBit = e.hash & bit; - Node lastRun = e, lo = null, hi = null; - for (Node p = e.next; p != null; p = p.next) { - int b = p.hash & bit; - if (b != runBit) { - runBit = b; - lastRun = p; - } - } - if (runBit == 0) - lo = lastRun; - else - hi = lastRun; - for (Node p = e; p != lastRun; p = p.next) { - int ph = p.hash & HASH_BITS; - Object pk = p.key, pv = p.val; - if ((ph & bit) == 0) - lo = new Node(ph, pk, pv, lo); - else - hi = new Node(ph, pk, pv, hi); - } - setTabAt(nextTab, i, lo); - setTabAt(nextTab, i + bit, hi); - } - - /** - * Splits a tree bin into lo and hi parts; installs in given table. - */ - private static void splitTreeBin(Node[] nextTab, int i, TreeBin t) { - int bit = nextTab.length >>> 1; - TreeBin lt = new TreeBin(); - TreeBin ht = new TreeBin(); - int lc = 0, hc = 0; - for (Node e = t.first; e != null; e = e.next) { - int h = e.hash & HASH_BITS; - Object k = e.key, v = e.val; - if ((h & bit) == 0) { - ++lc; - lt.putTreeNode(h, k, v); - } - else { - ++hc; - ht.putTreeNode(h, k, v); - } - } - Node ln, hn; // throw away trees if too small - if (lc <= (TREE_THRESHOLD >>> 1)) { - ln = null; - for (Node p = lt.first; p != null; p = p.next) - ln = new Node(p.hash, p.key, p.val, ln); - } - else - ln = new Node(MOVED, lt, null, null); - setTabAt(nextTab, i, ln); - if (hc <= (TREE_THRESHOLD >>> 1)) { - hn = null; - for (Node p = ht.first; p != null; p = p.next) - hn = new Node(p.hash, p.key, p.val, hn); - } - else - hn = new Node(MOVED, ht, null, null); - setTabAt(nextTab, i + bit, hn); - } - - /** - * Implementation for clear. Steps through each bin, removing all - * nodes. - */ - private final void internalClear() { - long delta = 0L; // negative number of deletions - int i = 0; - Node[] tab = table; - while (tab != null && i < tab.length) { - int fh; Object fk; - Node f = tabAt(tab, i); - if (f == null) - ++i; - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - for (Node p = t.first; p != null; p = p.next) { - if (p.val != null) { // (currently always true) - p.val = null; - --delta; - } - } - t.first = null; - t.root = null; - ++i; - } - } finally { - t.release(0); - } - } - else - tab = (Node[])fk; - } - else if ((fh & LOCKED) != 0) { - counter.add(delta); // opportunistically update count - delta = 0L; - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - try { - if (tabAt(tab, i) == f) { - for (Node e = f; e != null; e = e.next) { - if (e.val != null) { // (currently always true) - e.val = null; - --delta; - } - } - setTabAt(tab, i, null); - ++i; - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - } - } - if (delta != 0) - counter.add(delta); - } - - /* ----------------Table Traversal -------------- */ - - /** - * Encapsulates traversal for methods such as containsValue; also - * serves as a base class for other iterators and bulk tasks. - * - * At each step, the iterator snapshots the key ("nextKey") and - * value ("nextVal") of a valid node (i.e., one that, at point of - * snapshot, has a non-null user value). Because val fields can - * change (including to null, indicating deletion), field nextVal - * might not be accurate at point of use, but still maintains the - * weak consistency property of holding a value that was once - * valid. To support iterator.remove, the nextKey field is not - * updated (nulled out) when the iterator cannot advance. - * - * Internal traversals directly access these fields, as in: - * {@code while (it.advance() != null) { process(it.nextKey); }} - * - * Exported iterators must track whether the iterator has advanced - * (in hasNext vs next) (by setting/checking/nulling field - * nextVal), and then extract key, value, or key-value pairs as - * return values of next(). - * - * The iterator visits once each still-valid node that was - * reachable upon iterator construction. It might miss some that - * were added to a bin after the bin was visited, which is OK wrt - * consistency guarantees. Maintaining this property in the face - * of possible ongoing resizes requires a fair amount of - * bookkeeping state that is difficult to optimize away amidst - * volatile accesses. Even so, traversal maintains reasonable - * throughput. - * - * Normally, iteration proceeds bin-by-bin traversing lists. - * However, if the table has been resized, then all future steps - * must traverse both the bin at the current index as well as at - * (index + baseSize); and so on for further resizings. To - * paranoically cope with potential sharing by users of iterators - * across threads, iteration terminates if a bounds checks fails - * for a table read. - * - * This class extends ForkJoinTask to streamline parallel - * iteration in bulk operations (see BulkTask). This adds only an - * int of space overhead, which is close enough to negligible in - * cases where it is not needed to not worry about it. Because - * ForkJoinTask is Serializable, but iterators need not be, we - * need to add warning suppressions. - */ - @SuppressWarnings("serial") static class Traverser { - final ConcurrentHashMapV8 map; - Node next; // the next entry to use - K nextKey; // cached key field of next - V nextVal; // cached val field of next - Node[] tab; // current table; updated if resized - int index; // index of bin to use next - int baseIndex; // current index of initial table - int baseLimit; // index bound for initial table - int baseSize; // initial table size - - /** Creates iterator for all entries in the table. */ - Traverser(ConcurrentHashMapV8 map) { - this.map = map; - } - - /** Creates iterator for split() methods */ - Traverser(Traverser it) { - ConcurrentHashMapV8 m; Node[] t; - if ((m = this.map = it.map) == null) - t = null; - else if ((t = it.tab) == null && // force parent tab initialization - (t = it.tab = m.table) != null) - it.baseLimit = it.baseSize = t.length; - this.tab = t; - this.baseSize = it.baseSize; - it.baseLimit = this.index = this.baseIndex = - ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1; - } - - /** - * Advances next; returns nextVal or null if terminated. - * See above for explanation. - */ - final V advance() { - Node e = next; - V ev = null; - outer: do { - if (e != null) // advance past used/skipped node - e = e.next; - while (e == null) { // get to next non-null bin - ConcurrentHashMapV8 m; - Node[] t; int b, i, n; Object ek; // checks must use locals - if ((t = tab) != null) - n = t.length; - else if ((m = map) != null && (t = tab = m.table) != null) - n = baseLimit = baseSize = t.length; - else - break outer; - if ((b = baseIndex) >= baseLimit || - (i = index) < 0 || i >= n) - break outer; - if ((e = tabAt(t, i)) != null && e.hash == MOVED) { - if ((ek = e.key) instanceof TreeBin) - e = ((TreeBin)ek).first; - else { - tab = (Node[])ek; - continue; // restarts due to null val - } - } // visit upper slots if present - index = (i += baseSize) < n ? i : (baseIndex = b + 1); - } - nextKey = (K) e.key; - } while ((ev = (V) e.val) == null); // skip deleted or special nodes - next = e; - return nextVal = ev; - } - - public final void remove() { - Object k = nextKey; - if (k == null && (advance() == null || (k = nextKey) == null)) - throw new IllegalStateException(); - map.internalReplace(k, null, null); - } - - public final boolean hasNext() { - return nextVal != null || advance() != null; - } - - public final boolean hasMoreElements() { return hasNext(); } - public final void setRawResult(Object x) { } - public R getRawResult() { return null; } - public boolean exec() { return true; } - } - - /* ---------------- Public operations -------------- */ - - /** - * Creates a new, empty map with the default initial table size (16). - */ - public ConcurrentHashMapV8() { - this.counter = new LongAdder(); - } - - /** - * Creates a new, empty map with an initial table size - * accommodating the specified number of elements without the need - * to dynamically resize. - * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. - * @throws IllegalArgumentException if the initial capacity of - * elements is negative - */ - public ConcurrentHashMapV8(int initialCapacity) { - if (initialCapacity < 0) - throw new IllegalArgumentException(); - int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? - MAXIMUM_CAPACITY : - tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); - this.counter = new LongAdder(); - this.sizeCtl = cap; - } - - /** - * Creates a new map with the same mappings as the given map. - * - * @param m the map - */ - public ConcurrentHashMapV8(Map m) { - this.counter = new LongAdder(); - this.sizeCtl = DEFAULT_CAPACITY; - internalPutAll(m); - } - - /** - * Creates a new, empty map with an initial table size based on - * the given number of elements ({@code initialCapacity}) and - * initial table density ({@code loadFactor}). - * - * @param initialCapacity the initial capacity. The implementation - * performs internal sizing to accommodate this many elements, - * given the specified load factor. - * @param loadFactor the load factor (table density) for - * establishing the initial table size - * @throws IllegalArgumentException if the initial capacity of - * elements is negative or the load factor is nonpositive - * - * @since 1.6 - */ - public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { - this(initialCapacity, loadFactor, 1); - } - - /** - * Creates a new, empty map with an initial table size based on - * the given number of elements ({@code initialCapacity}), table - * density ({@code loadFactor}), and number of concurrently - * updating threads ({@code concurrencyLevel}). - * - * @param initialCapacity the initial capacity. The implementation - * performs internal sizing to accommodate this many elements, - * given the specified load factor. - * @param loadFactor the load factor (table density) for - * establishing the initial table size - * @param concurrencyLevel the estimated number of concurrently - * updating threads. The implementation may use this value as - * a sizing hint. - * @throws IllegalArgumentException if the initial capacity is - * negative or the load factor or concurrencyLevel are - * nonpositive - */ - public ConcurrentHashMapV8(int initialCapacity, - float loadFactor, int concurrencyLevel) { - if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) - throw new IllegalArgumentException(); - if (initialCapacity < concurrencyLevel) // Use at least as many bins - initialCapacity = concurrencyLevel; // as estimated threads - long size = (long)(1.0 + (long)initialCapacity / loadFactor); - int cap = (size >= (long)MAXIMUM_CAPACITY) ? - MAXIMUM_CAPACITY : tableSizeFor((int)size); - this.counter = new LongAdder(); - this.sizeCtl = cap; - } - - /** - * Creates a new {@link Set} backed by a ConcurrentHashMapV8 - * from the given type to {@code Boolean.TRUE}. - * - * @return the new set - */ - public static KeySetView newKeySet() { - return new KeySetView(new ConcurrentHashMapV8(), - Boolean.TRUE); - } - - /** - * Creates a new {@link Set} backed by a ConcurrentHashMapV8 - * from the given type to {@code Boolean.TRUE}. - * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. - * @throws IllegalArgumentException if the initial capacity of - * elements is negative - * @return the new set - */ - public static KeySetView newKeySet(int initialCapacity) { - return new KeySetView(new ConcurrentHashMapV8(initialCapacity), - Boolean.TRUE); - } - - /** - * {@inheritDoc} - */ - public boolean isEmpty() { - return counter.sum() <= 0L; // ignore transient negative values - } - - /** - * {@inheritDoc} - */ - public int size() { - long n = counter.sum(); - return ((n < 0L) ? 0 : - (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : - (int)n); - } - - /** - * Returns the number of mappings. This method should be used - * instead of {@link #size} because a ConcurrentHashMapV8 may - * contain more mappings than can be represented as an int. The - * value returned is a snapshot; the actual count may differ if - * there are ongoing concurrent insertions or removals. - * - * @return the number of mappings - */ - public long mappingCount() { - long n = counter.sum(); - return (n < 0L) ? 0L : n; // ignore transient negative values - } - - /** - * Returns the value to which the specified key is mapped, - * or {@code null} if this map contains no mapping for the key. - * - *

More formally, if this map contains a mapping from a key - * {@code k} to a value {@code v} such that {@code key.equals(k)}, - * then this method returns {@code v}; otherwise it returns - * {@code null}. (There can be at most one such mapping.) - * - * @throws NullPointerException if the specified key is null - */ - @SuppressWarnings("unchecked") public V get(Object key) { - if (key == null) - throw new NullPointerException(); - return (V)internalGet(key); - } - - /** - * Returns the value to which the specified key is mapped, - * or the given defaultValue if this map contains no mapping for the key. - * - * @param key the key - * @param defaultValue the value to return if this map contains - * no mapping for the given key - * @return the mapping for the key, if present; else the defaultValue - * @throws NullPointerException if the specified key is null - */ - @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) { - if (key == null) - throw new NullPointerException(); - V v = (V) internalGet(key); - return v == null ? defaultValue : v; - } - - /** - * Tests if the specified object is a key in this table. - * - * @param key possible key - * @return {@code true} if and only if the specified object - * is a key in this table, as determined by the - * {@code equals} method; {@code false} otherwise - * @throws NullPointerException if the specified key is null - */ - public boolean containsKey(Object key) { - if (key == null) - throw new NullPointerException(); - return internalGet(key) != null; - } - - /** - * Returns {@code true} if this map maps one or more keys to the - * specified value. Note: This method may require a full traversal - * of the map, and is much slower than method {@code containsKey}. - * - * @param value value whose presence in this map is to be tested - * @return {@code true} if this map maps one or more keys to the - * specified value - * @throws NullPointerException if the specified value is null - */ - public boolean containsValue(Object value) { - if (value == null) - throw new NullPointerException(); - Object v; - Traverser it = new Traverser(this); - while ((v = it.advance()) != null) { - if (v == value || value.equals(v)) - return true; - } - return false; - } - - public K findKey(Object value) { - if (value == null) - throw new NullPointerException(); - Object v; - Traverser it = new Traverser(this); - while ((v = it.advance()) != null) { - if (v == value || value.equals(v)) - return it.nextKey; - } - return null; - } - - /** - * Legacy method testing if some key maps into the specified value - * in this table. This method is identical in functionality to - * {@link #containsValue}, and exists solely to ensure - * full compatibility with class {@link java.util.Hashtable}, - * which supported this method prior to introduction of the - * Java Collections framework. - * - * @param value a value to search for - * @return {@code true} if and only if some key maps to the - * {@code value} argument in this table as - * determined by the {@code equals} method; - * {@code false} otherwise - * @throws NullPointerException if the specified value is null - */ - public boolean contains(Object value) { - return containsValue(value); - } - - /** - * Maps the specified key to the specified value in this table. - * Neither the key nor the value can be null. - * - *

The value can be retrieved by calling the {@code get} method - * with a key that is equal to the original key. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with {@code key}, or - * {@code null} if there was no mapping for {@code key} - * @throws NullPointerException if the specified key or value is null - */ - @SuppressWarnings("unchecked") public V put(K key, V value) { - if (key == null || value == null) - throw new NullPointerException(); - return (V)internalPut(key, value); - } - - /** - * {@inheritDoc} - * - * @return the previous value associated with the specified key, - * or {@code null} if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null - */ - @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { - if (key == null || value == null) - throw new NullPointerException(); - return (V)internalPutIfAbsent(key, value); - } - - /** - * Copies all of the mappings from the specified map to this one. - * These mappings replace any mappings that this map had for any of the - * keys currently in the specified map. - * - * @param m mappings to be stored in this map - */ - public void putAll(Map m) { - internalPutAll(m); - } - - /** - * If the specified key is not already associated with a value, - * computes its value using the given mappingFunction and enters - * it into the map unless null. This is equivalent to - *

 {@code
-     * if (map.containsKey(key))
-     *   return map.get(key);
-     * value = mappingFunction.apply(key);
-     * if (value != null)
-     *   map.put(key, value);
-     * return value;}
- * - * except that the action is performed atomically. If the - * function returns {@code null} no mapping is recorded. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and no mapping is recorded. Some - * attempted update operations on this map by other threads may be - * blocked while computation is in progress, so the computation - * should be short and simple, and must not attempt to update any - * other mappings of this Map. The most appropriate usage is to - * construct a new object serving as an initial mapped value, or - * memoized result, as in: - * - *
 {@code
-     * map.computeIfAbsent(key, new Fun() {
-     *   public V map(K k) { return new Value(f(k)); }});}
- * - * @param key key with which the specified value is to be associated - * @param mappingFunction the function to compute a value - * @return the current (existing or computed) value associated with - * the specified key, or null if the computed value is null - * @throws NullPointerException if the specified key or mappingFunction - * is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map that would - * otherwise never complete - * @throws RuntimeException or Error if the mappingFunction does so, - * in which case the mapping is left unestablished - */ - @SuppressWarnings("unchecked") public V computeIfAbsent - (K key, Fun mappingFunction) { - if (key == null || mappingFunction == null) - throw new NullPointerException(); - return (V)internalComputeIfAbsent(key, mappingFunction); - } - - /** - * If the given key is present, computes a new mapping value given a key and - * its current mapped value. This is equivalent to - *
 {@code
-     *   if (map.containsKey(key)) {
-     *     value = remappingFunction.apply(key, map.get(key));
-     *     if (value != null)
-     *       map.put(key, value);
-     *     else
-     *       map.remove(key);
-     *   }
-     * }
- * - * except that the action is performed atomically. If the - * function returns {@code null}, the mapping is removed. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and the current mapping is left - * unchanged. Some attempted update operations on this map by - * other threads may be blocked while computation is in progress, - * so the computation should be short and simple, and must not - * attempt to update any other mappings of this Map. For example, - * to either create or append new messages to a value mapping: - * - * @param key key with which the specified value is to be associated - * @param remappingFunction the function to compute a value - * @return the new value associated with the specified key, or null if none - * @throws NullPointerException if the specified key or remappingFunction - * is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map that would - * otherwise never complete - * @throws RuntimeException or Error if the remappingFunction does so, - * in which case the mapping is unchanged - */ - @SuppressWarnings("unchecked") public V computeIfPresent - (K key, BiFun remappingFunction) { - if (key == null || remappingFunction == null) - throw new NullPointerException(); - return (V)internalCompute(key, true, remappingFunction); - } - - /** - * Computes a new mapping value given a key and - * its current mapped value (or {@code null} if there is no current - * mapping). This is equivalent to - *
 {@code
-     *   value = remappingFunction.apply(key, map.get(key));
-     *   if (value != null)
-     *     map.put(key, value);
-     *   else
-     *     map.remove(key);
-     * }
- * - * except that the action is performed atomically. If the - * function returns {@code null}, the mapping is removed. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and the current mapping is left - * unchanged. Some attempted update operations on this map by - * other threads may be blocked while computation is in progress, - * so the computation should be short and simple, and must not - * attempt to update any other mappings of this Map. For example, - * to either create or append new messages to a value mapping: - * - *
 {@code
-     * Map map = ...;
-     * final String msg = ...;
-     * map.compute(key, new BiFun() {
-     *   public String apply(Key k, String v) {
-     *    return (v == null) ? msg : v + msg;});}}
- * - * @param key key with which the specified value is to be associated - * @param remappingFunction the function to compute a value - * @return the new value associated with the specified key, or null if none - * @throws NullPointerException if the specified key or remappingFunction - * is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map that would - * otherwise never complete - * @throws RuntimeException or Error if the remappingFunction does so, - * in which case the mapping is unchanged - */ - @SuppressWarnings("unchecked") public V compute - (K key, BiFun remappingFunction) { - if (key == null || remappingFunction == null) - throw new NullPointerException(); - return (V)internalCompute(key, false, remappingFunction); - } - - /** - * If the specified key is not already associated - * with a value, associate it with the given value. - * Otherwise, replace the value with the results of - * the given remapping function. This is equivalent to: - *
 {@code
-     *   if (!map.containsKey(key))
-     *     map.put(value);
-     *   else {
-     *     newValue = remappingFunction.apply(map.get(key), value);
-     *     if (value != null)
-     *       map.put(key, value);
-     *     else
-     *       map.remove(key);
-     *   }
-     * }
- * except that the action is performed atomically. If the - * function returns {@code null}, the mapping is removed. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and the current mapping is left - * unchanged. Some attempted update operations on this map by - * other threads may be blocked while computation is in progress, - * so the computation should be short and simple, and must not - * attempt to update any other mappings of this Map. - */ - @SuppressWarnings("unchecked") public V merge - (K key, V value, BiFun remappingFunction) { - if (key == null || value == null || remappingFunction == null) - throw new NullPointerException(); - return (V)internalMerge(key, value, remappingFunction); - } - - /** - * Removes the key (and its corresponding value) from this map. - * This method does nothing if the key is not in the map. - * - * @param key the key that needs to be removed - * @return the previous value associated with {@code key}, or - * {@code null} if there was no mapping for {@code key} - * @throws NullPointerException if the specified key is null - */ - @SuppressWarnings("unchecked") public V remove(Object key) { - if (key == null) - throw new NullPointerException(); - return (V)internalReplace(key, null, null); - } - - /** - * {@inheritDoc} - * - * @throws NullPointerException if the specified key is null - */ - public boolean remove(Object key, Object value) { - if (key == null) - throw new NullPointerException(); - if (value == null) - return false; - return internalReplace(key, null, value) != null; - } - - /** - * {@inheritDoc} - * - * @throws NullPointerException if any of the arguments are null - */ - public boolean replace(K key, V oldValue, V newValue) { - if (key == null || oldValue == null || newValue == null) - throw new NullPointerException(); - return internalReplace(key, newValue, oldValue) != null; - } - - /** - * {@inheritDoc} - * - * @return the previous value associated with the specified key, - * or {@code null} if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null - */ - @SuppressWarnings("unchecked") public V replace(K key, V value) { - if (key == null || value == null) - throw new NullPointerException(); - return (V)internalReplace(key, value, null); - } - - /** - * Removes all of the mappings from this map. - */ - public void clear() { - internalClear(); - } - - /** - * Returns a {@link Set} view of the keys contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa. - * - * @return the set view - */ - public KeySetView keySet() { - KeySetView ks = keySet; - return (ks != null) ? ks : (keySet = new KeySetView(this, null)); - } - - /** - * Returns a {@link Set} view of the keys in this map, using the - * given common mapped value for any additions (i.e., {@link - * Collection#add} and {@link Collection#addAll}). This is of - * course only appropriate if it is acceptable to use the same - * value for all additions from this view. - * - * @param mappedValue the mapped value to use for any - * additions. - * @return the set view - * @throws NullPointerException if the mappedValue is null - */ - public KeySetView keySet(V mappedValue) { - if (mappedValue == null) - throw new NullPointerException(); - return new KeySetView(this, mappedValue); - } - - /** - * Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are - * reflected in the collection, and vice-versa. - */ - public ValuesView values() { - ValuesView vs = values; - return (vs != null) ? vs : (values = new ValuesView(this)); - } - - /** - * Returns a {@link Set} view of the mappings contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa. The set supports element - * removal, which removes the corresponding mapping from the map, - * via the {@code Iterator.remove}, {@code Set.remove}, - * {@code removeAll}, {@code retainAll}, and {@code clear} - * operations. It does not support the {@code add} or - * {@code addAll} operations. - * - *

The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - */ - public Set> entrySet() { - EntrySetView es = entrySet; - return (es != null) ? es : (entrySet = new EntrySetView(this)); - } - - /** - * Returns an enumeration of the keys in this table. - * - * @return an enumeration of the keys in this table - * @see #keySet() - */ - public Enumeration keys() { - return new KeyIterator(this); - } - - /** - * Returns an enumeration of the values in this table. - * - * @return an enumeration of the values in this table - * @see #values() - */ - public Enumeration elements() { - return new ValueIterator(this); - } - - /** - * Returns a partitionable iterator of the keys in this map. - * - * @return a partitionable iterator of the keys in this map - */ - public Spliterator keySpliterator() { - return new KeyIterator(this); - } - - /** - * Returns a partitionable iterator of the values in this map. - * - * @return a partitionable iterator of the values in this map - */ - public Spliterator valueSpliterator() { - return new ValueIterator(this); - } - - /** - * Returns a partitionable iterator of the entries in this map. - * - * @return a partitionable iterator of the entries in this map - */ - public Spliterator> entrySpliterator() { - return new EntryIterator(this); - } - - /** - * Returns the hash code value for this {@link Map}, i.e., - * the sum of, for each key-value pair in the map, - * {@code key.hashCode() ^ value.hashCode()}. - * - * @return the hash code value for this map - */ - public int hashCode() { - int h = 0; - Traverser it = new Traverser(this); - Object v; - while ((v = it.advance()) != null) { - h += it.nextKey.hashCode() ^ v.hashCode(); - } - return h; - } - - /** - * Returns a string representation of this map. The string - * representation consists of a list of key-value mappings (in no - * particular order) enclosed in braces ("{@code {}}"). Adjacent - * mappings are separated by the characters {@code ", "} (comma - * and space). Each key-value mapping is rendered as the key - * followed by an equals sign ("{@code =}") followed by the - * associated value. - * - * @return a string representation of this map - */ - public String toString() { - Traverser it = new Traverser(this); - StringBuilder sb = new StringBuilder(); - sb.append('{'); - Object v; - if ((v = it.advance()) != null) { - for (;;) { - Object k = it.nextKey; - sb.append(k == this ? "(this Map)" : k); - sb.append('='); - sb.append(v == this ? "(this Map)" : v); - if ((v = it.advance()) == null) - break; - sb.append(',').append(' '); - } - } - return sb.append('}').toString(); - } - - /** - * Compares the specified object with this map for equality. - * Returns {@code true} if the given object is a map with the same - * mappings as this map. This operation may return misleading - * results if either map is concurrently modified during execution - * of this method. - * - * @param o object to be compared for equality with this map - * @return {@code true} if the specified object is equal to this map - */ - public boolean equals(Object o) { - if (o != this) { - if (!(o instanceof Map)) - return false; - Map m = (Map) o; - Traverser it = new Traverser(this); - Object val; - while ((val = it.advance()) != null) { - Object v = m.get(it.nextKey); - if (v == null || (v != val && !v.equals(val))) - return false; - } - for (Map.Entry e : m.entrySet()) { - Object mk, mv, v; - if ((mk = e.getKey()) == null || - (mv = e.getValue()) == null || - (v = internalGet(mk)) == null || - (mv != v && !mv.equals(v))) - return false; - } - } - return true; - } - - /* ----------------Iterators -------------- */ - - @SuppressWarnings("serial") static final class KeyIterator extends Traverser - implements Spliterator, Enumeration { - KeyIterator(ConcurrentHashMapV8 map) { super(map); } - KeyIterator(Traverser it) { - super(it); - } - public KeyIterator split() { - if (nextKey != null) - throw new IllegalStateException(); - return new KeyIterator(this); - } - @SuppressWarnings("unchecked") public final K next() { - if (nextVal == null && advance() == null) - throw new NoSuchElementException(); - Object k = nextKey; - nextVal = null; - return (K) k; - } - - public final K nextElement() { return next(); } - } - - @SuppressWarnings("serial") static final class ValueIterator extends Traverser - implements Spliterator, Enumeration { - ValueIterator(ConcurrentHashMapV8 map) { super(map); } - ValueIterator(Traverser it) { - super(it); - } - public ValueIterator split() { - if (nextKey != null) - throw new IllegalStateException(); - return new ValueIterator(this); - } - - @SuppressWarnings("unchecked") public final V next() { - Object v; - if ((v = nextVal) == null && (v = advance()) == null) - throw new NoSuchElementException(); - nextVal = null; - return (V) v; - } - - public final V nextElement() { return next(); } - } - - @SuppressWarnings("serial") static final class EntryIterator extends Traverser - implements Spliterator> { - EntryIterator(ConcurrentHashMapV8 map) { super(map); } - EntryIterator(Traverser it) { - super(it); - } - public EntryIterator split() { - if (nextKey != null) - throw new IllegalStateException(); - return new EntryIterator(this); - } - - @SuppressWarnings("unchecked") public final Map.Entry next() { - Object v; - if ((v = nextVal) == null && (v = advance()) == null) - throw new NoSuchElementException(); - Object k = nextKey; - nextVal = null; - return new MapEntry((K)k, (V)v, map); - } - } - - /** - * Exported Entry for iterators - */ - static final class MapEntry implements Map.Entry { - final K key; // non-null - V val; // non-null - final ConcurrentHashMapV8 map; - MapEntry(K key, V val, ConcurrentHashMapV8 map) { - this.key = key; - this.val = val; - this.map = map; - } - public final K getKey() { return key; } - public final V getValue() { return val; } - public final int hashCode() { return key.hashCode() ^ val.hashCode(); } - public final String toString(){ return key + "=" + val; } - - public final boolean equals(Object o) { - Object k, v; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry)o).getKey()) != null && - (v = e.getValue()) != null && - (k == key || k.equals(key)) && - (v == val || v.equals(val))); - } - - /** - * Sets our entry's value and writes through to the map. The - * value to return is somewhat arbitrary here. Since we do not - * necessarily track asynchronous changes, the most recent - * "previous" value could be different from what we return (or - * could even have been removed in which case the put will - * re-establish). We do not and cannot guarantee more. - */ - public final V setValue(V value) { - if (value == null) throw new NullPointerException(); - V v = val; - val = value; - map.put(key, value); - return v; - } - } - - /* ---------------- Serialization Support -------------- */ - - /** - * Stripped-down version of helper class used in previous version, - * declared for the sake of serialization compatibility - */ - static class Segment implements Serializable { - private static final long serialVersionUID = 2249069246763182397L; - final float loadFactor; - Segment(float lf) { this.loadFactor = lf; } - } - - /** - * Saves the state of the {@code ConcurrentHashMapV8} instance to a - * stream (i.e., serializes it). - * @param s the stream - * @serialData - * the key (Object) and value (Object) - * for each key-value mapping, followed by a null pair. - * The key-value mappings are emitted in no particular order. - */ - @SuppressWarnings("unchecked") private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - if (segments == null) { // for serialization compatibility - segments = (Segment[]) - new Segment[DEFAULT_CONCURRENCY_LEVEL]; - for (int i = 0; i < segments.length; ++i) - segments[i] = new Segment(LOAD_FACTOR); - } - s.defaultWriteObject(); - Traverser it = new Traverser(this); - Object v; - while ((v = it.advance()) != null) { - s.writeObject(it.nextKey); - s.writeObject(v); - } - s.writeObject(null); - s.writeObject(null); - segments = null; // throw away - } - - /** - * Reconstitutes the instance from a stream (that is, deserializes it). - * @param s the stream - */ - @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - this.segments = null; // unneeded - // initialize transient final field - UNSAFE.putObjectVolatile(this, counterOffset, new LongAdder()); - - // Create all nodes, then place in table once size is known - long size = 0L; - Node p = null; - for (;;) { - K k = (K) s.readObject(); - V v = (V) s.readObject(); - if (k != null && v != null) { - int h = spread(k.hashCode()); - p = new Node(h, k, v, p); - ++size; - } - else - break; - } - if (p != null) { - boolean init = false; - int n; - if (size >= (long)(MAXIMUM_CAPACITY >>> 1)) - n = MAXIMUM_CAPACITY; - else { - int sz = (int)size; - n = tableSizeFor(sz + (sz >>> 1) + 1); - } - int sc = sizeCtl; - boolean collide = false; - if (n > sc && - UNSAFE.compareAndSwapInt(this, sizeCtlOffset, sc, -1)) { - try { - if (table == null) { - init = true; - Node[] tab = new Node[n]; - int mask = n - 1; - while (p != null) { - int j = p.hash & mask; - Node next = p.next; - Node q = p.next = tabAt(tab, j); - setTabAt(tab, j, p); - if (!collide && q != null && q.hash == p.hash) - collide = true; - p = next; - } - table = tab; - counter.add(size); - sc = n - (n >>> 2); - } - } finally { - sizeCtl = sc; - } - if (collide) { // rescan and convert to TreeBins - Node[] tab = table; - for (int i = 0; i < tab.length; ++i) { - int c = 0; - for (Node e = tabAt(tab, i); e != null; e = e.next) { - if (++c > TREE_THRESHOLD && - (e.key instanceof Comparable)) { - replaceWithTreeBin(tab, i, e.key); - break; - } - } - } - } - } - if (!init) { // Can only happen if unsafely published. - while (p != null) { - internalPut(p.key, p.val); - p = p.next; - } - } - } - } - - - // ------------------------------------------------------- - - // Sams - /** Interface describing a void action of one argument */ - public interface Action { void apply(A a); } - /** Interface describing a void action of two arguments */ - public interface BiAction { void apply(A a, B b); } - /** Interface describing a function of one argument */ - public interface Generator { T apply(); } - /** Interface describing a function mapping its argument to a double */ - public interface ObjectToDouble { double apply(A a); } - /** Interface describing a function mapping its argument to a long */ - public interface ObjectToLong { long apply(A a); } - /** Interface describing a function mapping its argument to an int */ - public interface ObjectToInt {int apply(A a); } - /** Interface describing a function mapping two arguments to a double */ - public interface ObjectByObjectToDouble { double apply(A a, B b); } - /** Interface describing a function mapping two arguments to a long */ - public interface ObjectByObjectToLong { long apply(A a, B b); } - /** Interface describing a function mapping two arguments to an int */ - public interface ObjectByObjectToInt {int apply(A a, B b); } - /** Interface describing a function mapping a double to a double */ - public interface DoubleToDouble { double apply(double a); } - /** Interface describing a function mapping a long to a long */ - public interface LongToLong { long apply(long a); } - /** Interface describing a function mapping an int to an int */ - public interface IntToInt { int apply(int a); } - /** Interface describing a function mapping two doubles to a double */ - public interface DoubleByDoubleToDouble { double apply(double a, double b); } - /** Interface describing a function mapping two longs to a long */ - public interface LongByLongToLong { long apply(long a, long b); } - /** Interface describing a function mapping two ints to an int */ - public interface IntByIntToInt { int apply(int a, int b); } - - - /* ----------------Views -------------- */ - - /** - * Base class for views. - */ - static abstract class CHMView { - final ConcurrentHashMapV8 map; - CHMView(ConcurrentHashMapV8 map) { this.map = map; } - - /** - * Returns the map backing this view. - * - * @return the map backing this view - */ - public ConcurrentHashMapV8 getMap() { return map; } - - public final int size() { return map.size(); } - public final boolean isEmpty() { return map.isEmpty(); } - public final void clear() { map.clear(); } - - // implementations below rely on concrete classes supplying these - abstract public Iterator iterator(); - abstract public boolean contains(Object o); - abstract public boolean remove(Object o); - - private static final String oomeMsg = "Required array size too large"; - - public final Object[] toArray() { - long sz = map.mappingCount(); - if (sz > (long)(MAX_ARRAY_SIZE)) - throw new OutOfMemoryError(oomeMsg); - int n = (int)sz; - Object[] r = new Object[n]; - int i = 0; - Iterator it = iterator(); - while (it.hasNext()) { - if (i == n) { - if (n >= MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); - if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) - n = MAX_ARRAY_SIZE; - else - n += (n >>> 1) + 1; - r = Arrays.copyOf(r, n); - } - r[i++] = it.next(); - } - return (i == n) ? r : Arrays.copyOf(r, i); - } - - @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { - long sz = map.mappingCount(); - if (sz > (long)(MAX_ARRAY_SIZE)) - throw new OutOfMemoryError(oomeMsg); - int m = (int)sz; - T[] r = (a.length >= m) ? a : - (T[])java.lang.reflect.Array - .newInstance(a.getClass().getComponentType(), m); - int n = r.length; - int i = 0; - Iterator it = iterator(); - while (it.hasNext()) { - if (i == n) { - if (n >= MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); - if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) - n = MAX_ARRAY_SIZE; - else - n += (n >>> 1) + 1; - r = Arrays.copyOf(r, n); - } - r[i++] = (T)it.next(); - } - if (a == r && i < n) { - r[i] = null; // null-terminate - return r; - } - return (i == n) ? r : Arrays.copyOf(r, i); - } - - public final int hashCode() { - int h = 0; - for (Iterator it = iterator(); it.hasNext();) - h += it.next().hashCode(); - return h; - } - - public final String toString() { - StringBuilder sb = new StringBuilder(); - sb.append('['); - Iterator it = iterator(); - if (it.hasNext()) { - for (;;) { - Object e = it.next(); - sb.append(e == this ? "(this Collection)" : e); - if (!it.hasNext()) - break; - sb.append(',').append(' '); - } - } - return sb.append(']').toString(); - } - - public final boolean containsAll(Collection c) { - if (c != this) { - for (Iterator it = c.iterator(); it.hasNext();) { - Object e = it.next(); - if (e == null || !contains(e)) - return false; - } - } - return true; - } - - public final boolean removeAll(Collection c) { - boolean modified = false; - for (Iterator it = iterator(); it.hasNext();) { - if (c.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - public final boolean retainAll(Collection c) { - boolean modified = false; - for (Iterator it = iterator(); it.hasNext();) { - if (!c.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - } - - /** - * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in - * which additions may optionally be enabled by mapping to a - * common value. This class cannot be directly instantiated. See - * {@link #keySet}, {@link #keySet(Object)}, {@link #newKeySet()}, - * {@link #newKeySet(int)}. - */ - public static class KeySetView extends CHMView implements Set, java.io.Serializable { - private static final long serialVersionUID = 7249069246763182397L; - private final V value; - KeySetView(ConcurrentHashMapV8 map, V value) { // non-public - super(map); - this.value = value; - } - - /** - * Returns the default mapped value for additions, - * or {@code null} if additions are not supported. - * - * @return the default mapped value for additions, or {@code null} - * if not supported. - */ - public V getMappedValue() { return value; } - - // implement Set API - - public boolean contains(Object o) { return map.containsKey(o); } - public boolean remove(Object o) { return map.remove(o) != null; } - - /** - * Returns a "weakly consistent" iterator that will never - * throw {@link ConcurrentModificationException}, and - * guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not - * guaranteed to) reflect any modifications subsequent to - * construction. - * - * @return an iterator over the keys of this map - */ - public Iterator iterator() { return new KeyIterator(map); } - public boolean add(K e) { - V v; - if ((v = value) == null) - throw new UnsupportedOperationException(); - if (e == null) - throw new NullPointerException(); - return map.internalPutIfAbsent(e, v) == null; - } - public boolean addAll(Collection c) { - boolean added = false; - V v; - if ((v = value) == null) - throw new UnsupportedOperationException(); - for (K e : c) { - if (e == null) - throw new NullPointerException(); - if (map.internalPutIfAbsent(e, v) == null) - added = true; - } - return added; - } - public boolean equals(Object o) { - Set c; - return ((o instanceof Set) && - ((c = (Set)o) == this || - (containsAll(c) && c.containsAll(this)))); - } - } - - /** - * A view of a ConcurrentHashMapV8 as a {@link Collection} of - * values, in which additions are disabled. This class cannot be - * directly instantiated. See {@link #values}, - * - *

The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - */ - public static final class ValuesView extends CHMView - implements Collection { - ValuesView(ConcurrentHashMapV8 map) { super(map); } - public final boolean contains(Object o) { return map.containsValue(o); } - public final boolean remove(Object o) { - if (o != null) { - Iterator it = new ValueIterator(map); - while (it.hasNext()) { - if (o.equals(it.next())) { - it.remove(); - return true; - } - } - } - return false; - } - - /** - * Returns a "weakly consistent" iterator that will never - * throw {@link ConcurrentModificationException}, and - * guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not - * guaranteed to) reflect any modifications subsequent to - * construction. - * - * @return an iterator over the values of this map - */ - public final Iterator iterator() { - return new ValueIterator(map); - } - public final boolean add(V e) { - throw new UnsupportedOperationException(); - } - public final boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - } - - /** - * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) - * entries. This class cannot be directly instantiated. See - * {@link #entrySet}. - */ - public static final class EntrySetView extends CHMView - implements Set> { - EntrySetView(ConcurrentHashMapV8 map) { super(map); } - public final boolean contains(Object o) { - Object k, v, r; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry)o).getKey()) != null && - (r = map.get(k)) != null && - (v = e.getValue()) != null && - (v == r || v.equals(r))); - } - public final boolean remove(Object o) { - Object k, v; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry)o).getKey()) != null && - (v = e.getValue()) != null && - map.remove(k, v)); - } - - /** - * Returns a "weakly consistent" iterator that will never - * throw {@link ConcurrentModificationException}, and - * guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not - * guaranteed to) reflect any modifications subsequent to - * construction. - * - * @return an iterator over the entries of this map - */ - public final Iterator> iterator() { - return new EntryIterator(map); - } - - public final boolean add(Entry e) { - K key = e.getKey(); - V value = e.getValue(); - if (key == null || value == null) - throw new NullPointerException(); - return map.internalPut(key, value) == null; - } - public final boolean addAll(Collection> c) { - boolean added = false; - for (Entry e : c) { - if (add(e)) - added = true; - } - return added; - } - public boolean equals(Object o) { - Set c; - return ((o instanceof Set) && - ((c = (Set)o) == this || - (containsAll(c) && c.containsAll(this)))); - } - } - - // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long counterOffset; - private static final long sizeCtlOffset; - private static final long ABASE; - private static final int ASHIFT; - - static { - int ss; - try { - UNSAFE = getUnsafe(); - Class k = ConcurrentHashMapV8.class; - counterOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("counter")); - sizeCtlOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("sizeCtl")); - Class sc = Node[].class; - ABASE = UNSAFE.arrayBaseOffset(sc); - ss = UNSAFE.arrayIndexScale(sc); - } catch (Exception e) { - throw new Error(e); - } - if ((ss & (ss-1)) != 0) - throw new Error("data type scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(ss); - } - - /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. - * Replace with a simple call to Unsafe.getUnsafe when integrating - * into a jdk. - * - * @return a sun.misc.Unsafe - */ - private static sun.misc.Unsafe getUnsafe() { - try { - return sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException se) { - try { - return java.security.AccessController.doPrivileged - (new java.security - .PrivilegedExceptionAction() { - public sun.misc.Unsafe run() throws Exception { - java.lang.reflect.Field f = sun.misc - .Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return (sun.misc.Unsafe) f.get(null); - }}); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", - e.getCause()); - } - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,203 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on 1.9 version. - -package com.concurrent_ruby.ext.jsr166e; -import java.util.concurrent.atomic.AtomicLong; -import java.io.IOException; -import java.io.Serializable; -import java.io.ObjectInputStream; - -/** - * One or more variables that together maintain an initially zero - * {@code long} sum. When updates (method {@link #add}) are contended - * across threads, the set of variables may grow dynamically to reduce - * contention. Method {@link #sum} (or, equivalently, {@link - * #longValue}) returns the current total combined across the - * variables maintaining the sum. - * - *

This class is usually preferable to {@link AtomicLong} when - * multiple threads update a common sum that is used for purposes such - * as collecting statistics, not for fine-grained synchronization - * control. Under low update contention, the two classes have similar - * characteristics. But under high contention, expected throughput of - * this class is significantly higher, at the expense of higher space - * consumption. - * - *

This class extends {@link Number}, but does not define - * methods such as {@code hashCode} and {@code compareTo} because - * instances are expected to be mutated, and so are not useful as - * collection keys. - * - *

jsr166e note: This class is targeted to be placed in - * java.util.concurrent.atomic. - * - * @since 1.8 - * @author Doug Lea - */ -public class LongAdder extends Striped64 implements Serializable { - private static final long serialVersionUID = 7249069246863182397L; - - /** - * Version of plus for use in retryUpdate - */ - final long fn(long v, long x) { return v + x; } - - /** - * Creates a new adder with initial sum of zero. - */ - public LongAdder() { - } - - /** - * Adds the given value. - * - * @param x the value to add - */ - public void add(long x) { - Cell[] as; long b, v; HashCode hc; Cell a; int n; - if ((as = cells) != null || !casBase(b = base, b + x)) { - boolean uncontended = true; - int h = (hc = threadHashCode.get()).code; - if (as == null || (n = as.length) < 1 || - (a = as[(n - 1) & h]) == null || - !(uncontended = a.cas(v = a.value, v + x))) - retryUpdate(x, hc, uncontended); - } - } - - /** - * Equivalent to {@code add(1)}. - */ - public void increment() { - add(1L); - } - - /** - * Equivalent to {@code add(-1)}. - */ - public void decrement() { - add(-1L); - } - - /** - * Returns the current sum. The returned value is NOT an - * atomic snapshot: Invocation in the absence of concurrent - * updates returns an accurate result, but concurrent updates that - * occur while the sum is being calculated might not be - * incorporated. - * - * @return the sum - */ - public long sum() { - long sum = base; - Cell[] as = cells; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) - sum += a.value; - } - } - return sum; - } - - /** - * Resets variables maintaining the sum to zero. This method may - * be a useful alternative to creating a new adder, but is only - * effective if there are no concurrent updates. Because this - * method is intrinsically racy, it should only be used when it is - * known that no threads are concurrently updating. - */ - public void reset() { - internalReset(0L); - } - - /** - * Equivalent in effect to {@link #sum} followed by {@link - * #reset}. This method may apply for example during quiescent - * points between multithreaded computations. If there are - * updates concurrent with this method, the returned value is - * not guaranteed to be the final value occurring before - * the reset. - * - * @return the sum - */ - public long sumThenReset() { - long sum = base; - Cell[] as = cells; - base = 0L; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) { - sum += a.value; - a.value = 0L; - } - } - } - return sum; - } - - /** - * Returns the String representation of the {@link #sum}. - * @return the String representation of the {@link #sum} - */ - public String toString() { - return Long.toString(sum()); - } - - /** - * Equivalent to {@link #sum}. - * - * @return the sum - */ - public long longValue() { - return sum(); - } - - /** - * Returns the {@link #sum} as an {@code int} after a narrowing - * primitive conversion. - */ - public int intValue() { - return (int)sum(); - } - - /** - * Returns the {@link #sum} as a {@code float} - * after a widening primitive conversion. - */ - public float floatValue() { - return (float)sum(); - } - - /** - * Returns the {@link #sum} as a {@code double} after a widening - * primitive conversion. - */ - public double doubleValue() { - return (double)sum(); - } - - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeLong(sum()); - } - - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - s.defaultReadObject(); - busy = 0; - cells = null; - base = s.readLong(); - } - -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,342 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on 1.5 version. - -package com.concurrent_ruby.ext.jsr166e; -import java.util.Random; - -/** - * A package-local class holding common representation and mechanics - * for classes supporting dynamic striping on 64bit values. The class - * extends Number so that concrete subclasses must publicly do so. - */ -abstract class Striped64 extends Number { - /* - * This class maintains a lazily-initialized table of atomically - * updated variables, plus an extra "base" field. The table size - * is a power of two. Indexing uses masked per-thread hash codes. - * Nearly all declarations in this class are package-private, - * accessed directly by subclasses. - * - * Table entries are of class Cell; a variant of AtomicLong padded - * to reduce cache contention on most processors. Padding is - * overkill for most Atomics because they are usually irregularly - * scattered in memory and thus don't interfere much with each - * other. But Atomic objects residing in arrays will tend to be - * placed adjacent to each other, and so will most often share - * cache lines (with a huge negative performance impact) without - * this precaution. - * - * In part because Cells are relatively large, we avoid creating - * them until they are needed. When there is no contention, all - * updates are made to the base field. Upon first contention (a - * failed CAS on base update), the table is initialized to size 2. - * The table size is doubled upon further contention until - * reaching the nearest power of two greater than or equal to the - * number of CPUS. Table slots remain empty (null) until they are - * needed. - * - * A single spinlock ("busy") is used for initializing and - * resizing the table, as well as populating slots with new Cells. - * There is no need for a blocking lock: When the lock is not - * available, threads try other slots (or the base). During these - * retries, there is increased contention and reduced locality, - * which is still better than alternatives. - * - * Per-thread hash codes are initialized to random values. - * Contention and/or table collisions are indicated by failed - * CASes when performing an update operation (see method - * retryUpdate). Upon a collision, if the table size is less than - * the capacity, it is doubled in size unless some other thread - * holds the lock. If a hashed slot is empty, and lock is - * available, a new Cell is created. Otherwise, if the slot - * exists, a CAS is tried. Retries proceed by "double hashing", - * using a secondary hash (Marsaglia XorShift) to try to find a - * free slot. - * - * The table size is capped because, when there are more threads - * than CPUs, supposing that each thread were bound to a CPU, - * there would exist a perfect hash function mapping threads to - * slots that eliminates collisions. When we reach capacity, we - * search for this mapping by randomly varying the hash codes of - * colliding threads. Because search is random, and collisions - * only become known via CAS failures, convergence can be slow, - * and because threads are typically not bound to CPUS forever, - * may not occur at all. However, despite these limitations, - * observed contention rates are typically low in these cases. - * - * It is possible for a Cell to become unused when threads that - * once hashed to it terminate, as well as in the case where - * doubling the table causes no thread to hash to it under - * expanded mask. We do not try to detect or remove such cells, - * under the assumption that for long-running instances, observed - * contention levels will recur, so the cells will eventually be - * needed again; and for short-lived ones, it does not matter. - */ - - /** - * Padded variant of AtomicLong supporting only raw accesses plus CAS. - * The value field is placed between pads, hoping that the JVM doesn't - * reorder them. - * - * JVM intrinsics note: It would be possible to use a release-only - * form of CAS here, if it were provided. - */ - static final class Cell { - volatile long p0, p1, p2, p3, p4, p5, p6; - volatile long value; - volatile long q0, q1, q2, q3, q4, q5, q6; - Cell(long x) { value = x; } - - final boolean cas(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); - } - - // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long valueOffset; - static { - try { - UNSAFE = getUnsafe(); - Class ak = Cell.class; - valueOffset = UNSAFE.objectFieldOffset - (ak.getDeclaredField("value")); - } catch (Exception e) { - throw new Error(e); - } - } - - } - - /** - * Holder for the thread-local hash code. The code is initially - * random, but may be set to a different value upon collisions. - */ - static final class HashCode { - static final Random rng = new Random(); - int code; - HashCode() { - int h = rng.nextInt(); // Avoid zero to allow xorShift rehash - code = (h == 0) ? 1 : h; - } - } - - /** - * The corresponding ThreadLocal class - */ - static final class ThreadHashCode extends ThreadLocal { - public HashCode initialValue() { return new HashCode(); } - } - - /** - * Static per-thread hash codes. Shared across all instances to - * reduce ThreadLocal pollution and because adjustments due to - * collisions in one table are likely to be appropriate for - * others. - */ - static final ThreadHashCode threadHashCode = new ThreadHashCode(); - - /** Number of CPUS, to place bound on table size */ - static final int NCPU = Runtime.getRuntime().availableProcessors(); - - /** - * Table of cells. When non-null, size is a power of 2. - */ - transient volatile Cell[] cells; - - /** - * Base value, used mainly when there is no contention, but also as - * a fallback during table initialization races. Updated via CAS. - */ - transient volatile long base; - - /** - * Spinlock (locked via CAS) used when resizing and/or creating Cells. - */ - transient volatile int busy; - - /** - * Package-private default constructor - */ - Striped64() { - } - - /** - * CASes the base field. - */ - final boolean casBase(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); - } - - /** - * CASes the busy field from 0 to 1 to acquire lock. - */ - final boolean casBusy() { - return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); - } - - /** - * Computes the function of current and new value. Subclasses - * should open-code this update function for most uses, but the - * virtualized form is needed within retryUpdate. - * - * @param currentValue the current value (of either base or a cell) - * @param newValue the argument from a user update call - * @return result of the update function - */ - abstract long fn(long currentValue, long newValue); - - /** - * Handles cases of updates involving initialization, resizing, - * creating new Cells, and/or contention. See above for - * explanation. This method suffers the usual non-modularity - * problems of optimistic retry code, relying on rechecked sets of - * reads. - * - * @param x the value - * @param hc the hash code holder - * @param wasUncontended false if CAS failed before call - */ - final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { - int h = hc.code; - boolean collide = false; // True if last slot nonempty - for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { - if (busy == 0) { // Try to attach new Cell - Cell r = new Cell(x); // Optimistically create - if (busy == 0 && casBusy()) { - boolean created = false; - try { // Recheck under lock - Cell[] rs; int m, j; - if ((rs = cells) != null && - (m = rs.length) > 0 && - rs[j = (m - 1) & h] == null) { - rs[j] = r; - created = true; - } - } finally { - busy = 0; - } - if (created) - break; - continue; // Slot is now non-empty - } - } - collide = false; - } - else if (!wasUncontended) // CAS already known to fail - wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, fn(v, x))) - break; - else if (n >= NCPU || cells != as) - collide = false; // At max size or stale - else if (!collide) - collide = true; - else if (busy == 0 && casBusy()) { - try { - if (cells == as) { // Expand table unless stale - Cell[] rs = new Cell[n << 1]; - for (int i = 0; i < n; ++i) - rs[i] = as[i]; - cells = rs; - } - } finally { - busy = 0; - } - collide = false; - continue; // Retry with expanded table - } - h ^= h << 13; // Rehash - h ^= h >>> 17; - h ^= h << 5; - } - else if (busy == 0 && cells == as && casBusy()) { - boolean init = false; - try { // Initialize table - if (cells == as) { - Cell[] rs = new Cell[2]; - rs[h & 1] = new Cell(x); - cells = rs; - init = true; - } - } finally { - busy = 0; - } - if (init) - break; - } - else if (casBase(v = base, fn(v, x))) - break; // Fall back on using base - } - hc.code = h; // Record index for next time - } - - - /** - * Sets base and all cells to the given value. - */ - final void internalReset(long initialValue) { - Cell[] as = cells; - base = initialValue; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) - a.value = initialValue; - } - } - } - - // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long baseOffset; - private static final long busyOffset; - static { - try { - UNSAFE = getUnsafe(); - Class sk = Striped64.class; - baseOffset = UNSAFE.objectFieldOffset - (sk.getDeclaredField("base")); - busyOffset = UNSAFE.objectFieldOffset - (sk.getDeclaredField("busy")); - } catch (Exception e) { - throw new Error(e); - } - } - - /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. - * Replace with a simple call to Unsafe.getUnsafe when integrating - * into a jdk. - * - * @return a sun.misc.Unsafe - */ - private static sun.misc.Unsafe getUnsafe() { - try { - return sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException se) { - try { - return java.security.AccessController.doPrivileged - (new java.security - .PrivilegedExceptionAction() { - public sun.misc.Unsafe run() throws Exception { - java.lang.reflect.Field f = sun.misc - .Unsafe.class.getDeclaredField("theUnsafe"); - f.setAccessible(true); - return (sun.misc.Unsafe) f.get(null); - }}); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", - e.getCause()); - } - } - } - -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,3800 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on the 1.79 version. - -package com.concurrent_ruby.ext.jsr166e.nounsafe; - -import org.jruby.RubyClass; -import org.jruby.RubyNumeric; -import org.jruby.RubyObject; -import org.jruby.exceptions.RaiseException; -import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap; -import com.concurrent_ruby.ext.jsr166y.ThreadLocalRandom; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.builtin.IRubyObject; - -import java.util.Arrays; -import java.util.Map; -import java.util.Set; -import java.util.Collection; -import java.util.Hashtable; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Enumeration; -import java.util.ConcurrentModificationException; -import java.util.NoSuchElementException; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.concurrent.locks.AbstractQueuedSynchronizer; - -import java.io.Serializable; - -/** - * A hash table supporting full concurrency of retrievals and - * high expected concurrency for updates. This class obeys the - * same functional specification as {@link java.util.Hashtable}, and - * includes versions of methods corresponding to each method of - * {@code Hashtable}. However, even though all operations are - * thread-safe, retrieval operations do not entail locking, - * and there is not any support for locking the entire table - * in a way that prevents all access. This class is fully - * interoperable with {@code Hashtable} in programs that rely on its - * thread safety but not on its synchronization details. - * - *

Retrieval operations (including {@code get}) generally do not - * block, so may overlap with update operations (including {@code put} - * and {@code remove}). Retrievals reflect the results of the most - * recently completed update operations holding upon their - * onset. (More formally, an update operation for a given key bears a - * happens-before relation with any (non-null) retrieval for - * that key reporting the updated value.) For aggregate operations - * such as {@code putAll} and {@code clear}, concurrent retrievals may - * reflect insertion or removal of only some entries. Similarly, - * Iterators and Enumerations return elements reflecting the state of - * the hash table at some point at or since the creation of the - * iterator/enumeration. They do not throw {@link - * ConcurrentModificationException}. However, iterators are designed - * to be used by only one thread at a time. Bear in mind that the - * results of aggregate status methods including {@code size}, {@code - * isEmpty}, and {@code containsValue} are typically useful only when - * a map is not undergoing concurrent updates in other threads. - * Otherwise the results of these methods reflect transient states - * that may be adequate for monitoring or estimation purposes, but not - * for program control. - * - *

The table is dynamically expanded when there are too many - * collisions (i.e., keys that have distinct hash codes but fall into - * the same slot modulo the table size), with the expected average - * effect of maintaining roughly two bins per mapping (corresponding - * to a 0.75 load factor threshold for resizing). There may be much - * variance around this average as mappings are added and removed, but - * overall, this maintains a commonly accepted time/space tradeoff for - * hash tables. However, resizing this or any other kind of hash - * table may be a relatively slow operation. When possible, it is a - * good idea to provide a size estimate as an optional {@code - * initialCapacity} constructor argument. An additional optional - * {@code loadFactor} constructor argument provides a further means of - * customizing initial table capacity by specifying the table density - * to be used in calculating the amount of space to allocate for the - * given number of elements. Also, for compatibility with previous - * versions of this class, constructors may optionally specify an - * expected {@code concurrencyLevel} as an additional hint for - * internal sizing. Note that using many keys with exactly the same - * {@code hashCode()} is a sure way to slow down performance of any - * hash table. - * - *

A {@link Set} projection of a ConcurrentHashMapV8 may be created - * (using {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed - * (using {@link #keySet(Object)} when only keys are of interest, and the - * mapped values are (perhaps transiently) not used or all take the - * same mapping value. - * - *

A ConcurrentHashMapV8 can be used as scalable frequency map (a - * form of histogram or multiset) by using {@link LongAdder} values - * and initializing via {@link #computeIfAbsent}. For example, to add - * a count to a {@code ConcurrentHashMapV8 freqs}, you - * can use {@code freqs.computeIfAbsent(k -> new - * LongAdder()).increment();} - * - *

This class and its views and iterators implement all of the - * optional methods of the {@link Map} and {@link Iterator} - * interfaces. - * - *

Like {@link Hashtable} but unlike {@link HashMap}, this class - * does not allow {@code null} to be used as a key or value. - * - *

ConcurrentHashMapV8s support parallel operations using the {@link - * ForkJoinPool#commonPool}. (Tasks that may be used in other contexts - * are available in class {@link ForkJoinTasks}). These operations are - * designed to be safely, and often sensibly, applied even with maps - * that are being concurrently updated by other threads; for example, - * when computing a snapshot summary of the values in a shared - * registry. There are three kinds of operation, each with four - * forms, accepting functions with Keys, Values, Entries, and (Key, - * Value) arguments and/or return values. (The first three forms are - * also available via the {@link #keySet()}, {@link #values()} and - * {@link #entrySet()} views). Because the elements of a - * ConcurrentHashMapV8 are not ordered in any particular way, and may be - * processed in different orders in different parallel executions, the - * correctness of supplied functions should not depend on any - * ordering, or on any other objects or values that may transiently - * change while computation is in progress; and except for forEach - * actions, should ideally be side-effect-free. - * - *

- * - *

The concurrency properties of bulk operations follow - * from those of ConcurrentHashMapV8: Any non-null result returned - * from {@code get(key)} and related access methods bears a - * happens-before relation with the associated insertion or - * update. The result of any bulk operation reflects the - * composition of these per-element relations (but is not - * necessarily atomic with respect to the map as a whole unless it - * is somehow known to be quiescent). Conversely, because keys - * and values in the map are never null, null serves as a reliable - * atomic indicator of the current lack of any result. To - * maintain this property, null serves as an implicit basis for - * all non-scalar reduction operations. For the double, long, and - * int versions, the basis should be one that, when combined with - * any other value, returns that other value (more formally, it - * should be the identity element for the reduction). Most common - * reductions have these properties; for example, computing a sum - * with basis 0 or a minimum with basis MAX_VALUE. - * - *

Search and transformation functions provided as arguments - * should similarly return null to indicate the lack of any result - * (in which case it is not used). In the case of mapped - * reductions, this also enables transformations to serve as - * filters, returning null (or, in the case of primitive - * specializations, the identity basis) if the element should not - * be combined. You can create compound transformations and - * filterings by composing them yourself under this "null means - * there is nothing there now" rule before using them in search or - * reduce operations. - * - *

Methods accepting and/or returning Entry arguments maintain - * key-value associations. They may be useful for example when - * finding the key for the greatest value. Note that "plain" Entry - * arguments can be supplied using {@code new - * AbstractMap.SimpleEntry(k,v)}. - * - *

Bulk operations may complete abruptly, throwing an - * exception encountered in the application of a supplied - * function. Bear in mind when handling such exceptions that other - * concurrently executing functions could also have thrown - * exceptions, or would have done so if the first exception had - * not occurred. - * - *

Parallel speedups for bulk operations compared to sequential - * processing are common but not guaranteed. Operations involving - * brief functions on small maps may execute more slowly than - * sequential loops if the underlying work to parallelize the - * computation is more expensive than the computation itself. - * Similarly, parallelization may not lead to much actual parallelism - * if all processors are busy performing unrelated tasks. - * - *

All arguments to all task methods must be non-null. - * - *

jsr166e note: During transition, this class - * uses nested functional interfaces with different names but the - * same forms as those expected for JDK8. - * - *

This class is a member of the - * - * Java Collections Framework. - * - * @since 1.5 - * @author Doug Lea - * @param the type of keys maintained by this map - * @param the type of mapped values - */ -public class ConcurrentHashMapV8 - implements ConcurrentMap, Serializable, ConcurrentHashMap { - private static final long serialVersionUID = 7249069246763182397L; - - /** - * A partitionable iterator. A Spliterator can be traversed - * directly, but can also be partitioned (before traversal) by - * creating another Spliterator that covers a non-overlapping - * portion of the elements, and so may be amenable to parallel - * execution. - * - *

This interface exports a subset of expected JDK8 - * functionality. - * - *

Sample usage: Here is one (of the several) ways to compute - * the sum of the values held in a map using the ForkJoin - * framework. As illustrated here, Spliterators are well suited to - * designs in which a task repeatedly splits off half its work - * into forked subtasks until small enough to process directly, - * and then joins these subtasks. Variants of this style can also - * be used in completion-based designs. - * - *

-     * {@code ConcurrentHashMapV8 m = ...
-     * // split as if have 8 * parallelism, for load balance
-     * int n = m.size();
-     * int p = aForkJoinPool.getParallelism() * 8;
-     * int split = (n < p)? n : p;
-     * long sum = aForkJoinPool.invoke(new SumValues(m.valueSpliterator(), split, null));
-     * // ...
-     * static class SumValues extends RecursiveTask {
-     *   final Spliterator s;
-     *   final int split;             // split while > 1
-     *   final SumValues nextJoin;    // records forked subtasks to join
-     *   SumValues(Spliterator s, int depth, SumValues nextJoin) {
-     *     this.s = s; this.depth = depth; this.nextJoin = nextJoin;
-     *   }
-     *   public Long compute() {
-     *     long sum = 0;
-     *     SumValues subtasks = null; // fork subtasks
-     *     for (int s = split >>> 1; s > 0; s >>>= 1)
-     *       (subtasks = new SumValues(s.split(), s, subtasks)).fork();
-     *     while (s.hasNext())        // directly process remaining elements
-     *       sum += s.next();
-     *     for (SumValues t = subtasks; t != null; t = t.nextJoin)
-     *       sum += t.join();         // collect subtask results
-     *     return sum;
-     *   }
-     * }
-     * }
- */ - public static interface Spliterator extends Iterator { - /** - * Returns a Spliterator covering approximately half of the - * elements, guaranteed not to overlap with those subsequently - * returned by this Spliterator. After invoking this method, - * the current Spliterator will not produce any of - * the elements of the returned Spliterator, but the two - * Spliterators together will produce all of the elements that - * would have been produced by this Spliterator had this - * method not been called. The exact number of elements - * produced by the returned Spliterator is not guaranteed, and - * may be zero (i.e., with {@code hasNext()} reporting {@code - * false}) if this Spliterator cannot be further split. - * - * @return a Spliterator covering approximately half of the - * elements - * @throws IllegalStateException if this Spliterator has - * already commenced traversing elements - */ - Spliterator split(); - } - - - /* - * Overview: - * - * The primary design goal of this hash table is to maintain - * concurrent readability (typically method get(), but also - * iterators and related methods) while minimizing update - * contention. Secondary goals are to keep space consumption about - * the same or better than java.util.HashMap, and to support high - * initial insertion rates on an empty table by many threads. - * - * Each key-value mapping is held in a Node. Because Node fields - * can contain special values, they are defined using plain Object - * types. Similarly in turn, all internal methods that use them - * work off Object types. And similarly, so do the internal - * methods of auxiliary iterator and view classes. All public - * generic typed methods relay in/out of these internal methods, - * supplying null-checks and casts as needed. This also allows - * many of the public methods to be factored into a smaller number - * of internal methods (although sadly not so for the five - * variants of put-related operations). The validation-based - * approach explained below leads to a lot of code sprawl because - * retry-control precludes factoring into smaller methods. - * - * The table is lazily initialized to a power-of-two size upon the - * first insertion. Each bin in the table normally contains a - * list of Nodes (most often, the list has only zero or one Node). - * Table accesses require volatile/atomic reads, writes, and - * CASes. Because there is no other way to arrange this without - * adding further indirections, we use intrinsics - * (sun.misc.Unsafe) operations. The lists of nodes within bins - * are always accurately traversable under volatile reads, so long - * as lookups check hash code and non-nullness of value before - * checking key equality. - * - * We use the top two bits of Node hash fields for control - * purposes -- they are available anyway because of addressing - * constraints. As explained further below, these top bits are - * used as follows: - * 00 - Normal - * 01 - Locked - * 11 - Locked and may have a thread waiting for lock - * 10 - Node is a forwarding node - * - * The lower 30 bits of each Node's hash field contain a - * transformation of the key's hash code, except for forwarding - * nodes, for which the lower bits are zero (and so always have - * hash field == MOVED). - * - * Insertion (via put or its variants) of the first node in an - * empty bin is performed by just CASing it to the bin. This is - * by far the most common case for put operations under most - * key/hash distributions. Other update operations (insert, - * delete, and replace) require locks. We do not want to waste - * the space required to associate a distinct lock object with - * each bin, so instead use the first node of a bin list itself as - * a lock. Blocking support for these locks relies on the builtin - * "synchronized" monitors. However, we also need a tryLock - * construction, so we overlay these by using bits of the Node - * hash field for lock control (see above), and so normally use - * builtin monitors only for blocking and signalling using - * wait/notifyAll constructions. See Node.tryAwaitLock. - * - * Using the first node of a list as a lock does not by itself - * suffice though: When a node is locked, any update must first - * validate that it is still the first node after locking it, and - * retry if not. Because new nodes are always appended to lists, - * once a node is first in a bin, it remains first until deleted - * or the bin becomes invalidated (upon resizing). However, - * operations that only conditionally update may inspect nodes - * until the point of update. This is a converse of sorts to the - * lazy locking technique described by Herlihy & Shavit. - * - * The main disadvantage of per-bin locks is that other update - * operations on other nodes in a bin list protected by the same - * lock can stall, for example when user equals() or mapping - * functions take a long time. However, statistically, under - * random hash codes, this is not a common problem. Ideally, the - * frequency of nodes in bins follows a Poisson distribution - * (http://en.wikipedia.org/wiki/Poisson_distribution) with a - * parameter of about 0.5 on average, given the resizing threshold - * of 0.75, although with a large variance because of resizing - * granularity. Ignoring variance, the expected occurrences of - * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The - * first values are: - * - * 0: 0.60653066 - * 1: 0.30326533 - * 2: 0.07581633 - * 3: 0.01263606 - * 4: 0.00157952 - * 5: 0.00015795 - * 6: 0.00001316 - * 7: 0.00000094 - * 8: 0.00000006 - * more: less than 1 in ten million - * - * Lock contention probability for two threads accessing distinct - * elements is roughly 1 / (8 * #elements) under random hashes. - * - * Actual hash code distributions encountered in practice - * sometimes deviate significantly from uniform randomness. This - * includes the case when N > (1<<30), so some keys MUST collide. - * Similarly for dumb or hostile usages in which multiple keys are - * designed to have identical hash codes. Also, although we guard - * against the worst effects of this (see method spread), sets of - * hashes may differ only in bits that do not impact their bin - * index for a given power-of-two mask. So we use a secondary - * strategy that applies when the number of nodes in a bin exceeds - * a threshold, and at least one of the keys implements - * Comparable. These TreeBins use a balanced tree to hold nodes - * (a specialized form of red-black trees), bounding search time - * to O(log N). Each search step in a TreeBin is around twice as - * slow as in a regular list, but given that N cannot exceed - * (1<<64) (before running out of addresses) this bounds search - * steps, lock hold times, etc, to reasonable constants (roughly - * 100 nodes inspected per operation worst case) so long as keys - * are Comparable (which is very common -- String, Long, etc). - * TreeBin nodes (TreeNodes) also maintain the same "next" - * traversal pointers as regular nodes, so can be traversed in - * iterators in the same way. - * - * The table is resized when occupancy exceeds a percentage - * threshold (nominally, 0.75, but see below). Only a single - * thread performs the resize (using field "sizeCtl", to arrange - * exclusion), but the table otherwise remains usable for reads - * and updates. Resizing proceeds by transferring bins, one by - * one, from the table to the next table. Because we are using - * power-of-two expansion, the elements from each bin must either - * stay at same index, or move with a power of two offset. We - * eliminate unnecessary node creation by catching cases where old - * nodes can be reused because their next fields won't change. On - * average, only about one-sixth of them need cloning when a table - * doubles. The nodes they replace will be garbage collectable as - * soon as they are no longer referenced by any reader thread that - * may be in the midst of concurrently traversing table. Upon - * transfer, the old table bin contains only a special forwarding - * node (with hash field "MOVED") that contains the next table as - * its key. On encountering a forwarding node, access and update - * operations restart, using the new table. - * - * Each bin transfer requires its bin lock. However, unlike other - * cases, a transfer can skip a bin if it fails to acquire its - * lock, and revisit it later (unless it is a TreeBin). Method - * rebuild maintains a buffer of TRANSFER_BUFFER_SIZE bins that - * have been skipped because of failure to acquire a lock, and - * blocks only if none are available (i.e., only very rarely). - * The transfer operation must also ensure that all accessible - * bins in both the old and new table are usable by any traversal. - * When there are no lock acquisition failures, this is arranged - * simply by proceeding from the last bin (table.length - 1) up - * towards the first. Upon seeing a forwarding node, traversals - * (see class Iter) arrange to move to the new table - * without revisiting nodes. However, when any node is skipped - * during a transfer, all earlier table bins may have become - * visible, so are initialized with a reverse-forwarding node back - * to the old table until the new ones are established. (This - * sometimes requires transiently locking a forwarding node, which - * is possible under the above encoding.) These more expensive - * mechanics trigger only when necessary. - * - * The traversal scheme also applies to partial traversals of - * ranges of bins (via an alternate Traverser constructor) - * to support partitioned aggregate operations. Also, read-only - * operations give up if ever forwarded to a null table, which - * provides support for shutdown-style clearing, which is also not - * currently implemented. - * - * Lazy table initialization minimizes footprint until first use, - * and also avoids resizings when the first operation is from a - * putAll, constructor with map argument, or deserialization. - * These cases attempt to override the initial capacity settings, - * but harmlessly fail to take effect in cases of races. - * - * The element count is maintained using a LongAdder, which avoids - * contention on updates but can encounter cache thrashing if read - * too frequently during concurrent access. To avoid reading so - * often, resizing is attempted either when a bin lock is - * contended, or upon adding to a bin already holding two or more - * nodes (checked before adding in the xIfAbsent methods, after - * adding in others). Under uniform hash distributions, the - * probability of this occurring at threshold is around 13%, - * meaning that only about 1 in 8 puts check threshold (and after - * resizing, many fewer do so). But this approximation has high - * variance for small table sizes, so we check on any collision - * for sizes <= 64. The bulk putAll operation further reduces - * contention by only committing count updates upon these size - * checks. - * - * Maintaining API and serialization compatibility with previous - * versions of this class introduces several oddities. Mainly: We - * leave untouched but unused constructor arguments refering to - * concurrencyLevel. We accept a loadFactor constructor argument, - * but apply it only to initial table capacity (which is the only - * time that we can guarantee to honor it.) We also declare an - * unused "Segment" class that is instantiated in minimal form - * only when serializing. - */ - - /* ---------------- Constants -------------- */ - - /** - * The largest possible table capacity. This value must be - * exactly 1<<30 to stay within Java array allocation and indexing - * bounds for power of two table sizes, and is further required - * because the top two bits of 32bit hash fields are used for - * control purposes. - */ - private static final int MAXIMUM_CAPACITY = 1 << 30; - - /** - * The default initial table capacity. Must be a power of 2 - * (i.e., at least 1) and at most MAXIMUM_CAPACITY. - */ - private static final int DEFAULT_CAPACITY = 16; - - /** - * The largest possible (non-power of two) array size. - * Needed by toArray and related methods. - */ - static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - - /** - * The default concurrency level for this table. Unused but - * defined for compatibility with previous versions of this class. - */ - private static final int DEFAULT_CONCURRENCY_LEVEL = 16; - - /** - * The load factor for this table. Overrides of this value in - * constructors affect only the initial table capacity. The - * actual floating point value isn't normally used -- it is - * simpler to use expressions such as {@code n - (n >>> 2)} for - * the associated resizing threshold. - */ - private static final float LOAD_FACTOR = 0.75f; - - /** - * The buffer size for skipped bins during transfers. The - * value is arbitrary but should be large enough to avoid - * most locking stalls during resizes. - */ - private static final int TRANSFER_BUFFER_SIZE = 32; - - /** - * The bin count threshold for using a tree rather than list for a - * bin. The value reflects the approximate break-even point for - * using tree-based operations. - * Note that Doug's version defaults to 8, but when dealing with - * Ruby objects it is actually beneficial to avoid TreeNodes - * as long as possible as it usually means going into Ruby land. - */ - private static final int TREE_THRESHOLD = 16; - - /* - * Encodings for special uses of Node hash fields. See above for - * explanation. - */ - static final int MOVED = 0x80000000; // hash field for forwarding nodes - static final int LOCKED = 0x40000000; // set/tested only as a bit - static final int WAITING = 0xc0000000; // both bits set/tested together - static final int HASH_BITS = 0x3fffffff; // usable bits of normal node hash - - /* ---------------- Fields -------------- */ - - /** - * The array of bins. Lazily initialized upon first insertion. - * Size is always a power of two. Accessed directly by iterators. - */ - transient volatile AtomicReferenceArray table; - - /** - * The counter maintaining number of elements. - */ - private transient LongAdder counter; - - /** - * Table initialization and resizing control. When negative, the - * table is being initialized or resized. Otherwise, when table is - * null, holds the initial table size to use upon creation, or 0 - * for default. After initialization, holds the next element count - * value upon which to resize the table. - */ - private transient volatile int sizeCtl; - - // views - private transient KeySetView keySet; - private transient ValuesView values; - private transient EntrySetView entrySet; - - /** For serialization compatibility. Null unless serialized; see below */ - private Segment[] segments; - - static AtomicIntegerFieldUpdater SIZE_CTRL_UPDATER = AtomicIntegerFieldUpdater.newUpdater(ConcurrentHashMapV8.class, "sizeCtl"); - - /* ---------------- Table element access -------------- */ - - /* - * Volatile access methods are used for table elements as well as - * elements of in-progress next table while resizing. Uses are - * null checked by callers, and implicitly bounds-checked, relying - * on the invariants that tab arrays have non-zero size, and all - * indices are masked with (tab.length - 1) which is never - * negative and always less than length. Note that, to be correct - * wrt arbitrary concurrency errors by users, bounds checks must - * operate on local variables, which accounts for some odd-looking - * inline assignments below. - */ - - static final Node tabAt(AtomicReferenceArray tab, int i) { // used by Iter - return tab.get(i); - } - - private static final boolean casTabAt(AtomicReferenceArray tab, int i, Node c, Node v) { - return tab.compareAndSet(i, c, v); - } - - private static final void setTabAt(AtomicReferenceArray tab, int i, Node v) { - tab.set(i, v); - } - - /* ---------------- Nodes -------------- */ - - /** - * Key-value entry. Note that this is never exported out as a - * user-visible Map.Entry (see MapEntry below). Nodes with a hash - * field of MOVED are special, and do not contain user keys or - * values. Otherwise, keys are never null, and null val fields - * indicate that a node is in the process of being deleted or - * created. For purposes of read-only access, a key may be read - * before a val, but can only be used after checking val to be - * non-null. - */ - static class Node { - volatile int hash; - final Object key; - volatile Object val; - volatile Node next; - - static AtomicIntegerFieldUpdater HASH_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Node.class, "hash"); - - Node(int hash, Object key, Object val, Node next) { - this.hash = hash; - this.key = key; - this.val = val; - this.next = next; - } - - /** CompareAndSet the hash field */ - final boolean casHash(int cmp, int val) { - return HASH_UPDATER.compareAndSet(this, cmp, val); - } - - /** The number of spins before blocking for a lock */ - static final int MAX_SPINS = - Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; - - /** - * Spins a while if LOCKED bit set and this node is the first - * of its bin, and then sets WAITING bits on hash field and - * blocks (once) if they are still set. It is OK for this - * method to return even if lock is not available upon exit, - * which enables these simple single-wait mechanics. - * - * The corresponding signalling operation is performed within - * callers: Upon detecting that WAITING has been set when - * unlocking lock (via a failed CAS from non-waiting LOCKED - * state), unlockers acquire the sync lock and perform a - * notifyAll. - * - * The initial sanity check on tab and bounds is not currently - * necessary in the only usages of this method, but enables - * use in other future contexts. - */ - final void tryAwaitLock(AtomicReferenceArray tab, int i) { - if (tab != null && i >= 0 && i < tab.length()) { // sanity check - int r = ThreadLocalRandom.current().nextInt(); // randomize spins - int spins = MAX_SPINS, h; - while (tabAt(tab, i) == this && ((h = hash) & LOCKED) != 0) { - if (spins >= 0) { - r ^= r << 1; r ^= r >>> 3; r ^= r << 10; // xorshift - if (r >= 0 && --spins == 0) - Thread.yield(); // yield before block - } - else if (casHash(h, h | WAITING)) { - synchronized (this) { - if (tabAt(tab, i) == this && - (hash & WAITING) == WAITING) { - try { - wait(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } - } - else - notifyAll(); // possibly won race vs signaller - } - break; - } - } - } - } - } - - /* ---------------- TreeBins -------------- */ - - /** - * Nodes for use in TreeBins - */ - static final class TreeNode extends Node { - TreeNode parent; // red-black tree links - TreeNode left; - TreeNode right; - TreeNode prev; // needed to unlink next upon deletion - boolean red; - - TreeNode(int hash, Object key, Object val, Node next, TreeNode parent) { - super(hash, key, val, next); - this.parent = parent; - } - } - - /** - * A specialized form of red-black tree for use in bins - * whose size exceeds a threshold. - * - * TreeBins use a special form of comparison for search and - * related operations (which is the main reason we cannot use - * existing collections such as TreeMaps). TreeBins contain - * Comparable elements, but may contain others, as well as - * elements that are Comparable but not necessarily Comparable - * for the same T, so we cannot invoke compareTo among them. To - * handle this, the tree is ordered primarily by hash value, then - * by getClass().getName() order, and then by Comparator order - * among elements of the same class. On lookup at a node, if - * elements are not comparable or compare as 0, both left and - * right children may need to be searched in the case of tied hash - * values. (This corresponds to the full list search that would be - * necessary if all elements were non-Comparable and had tied - * hashes.) The red-black balancing code is updated from - * pre-jdk-collections - * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) - * based in turn on Cormen, Leiserson, and Rivest "Introduction to - * Algorithms" (CLR). - * - * TreeBins also maintain a separate locking discipline than - * regular bins. Because they are forwarded via special MOVED - * nodes at bin heads (which can never change once established), - * we cannot use those nodes as locks. Instead, TreeBin - * extends AbstractQueuedSynchronizer to support a simple form of - * read-write lock. For update operations and table validation, - * the exclusive form of lock behaves in the same way as bin-head - * locks. However, lookups use shared read-lock mechanics to allow - * multiple readers in the absence of writers. Additionally, - * these lookups do not ever block: While the lock is not - * available, they proceed along the slow traversal path (via - * next-pointers) until the lock becomes available or the list is - * exhausted, whichever comes first. (These cases are not fast, - * but maximize aggregate expected throughput.) The AQS mechanics - * for doing this are straightforward. The lock state is held as - * AQS getState(). Read counts are negative; the write count (1) - * is positive. There are no signalling preferences among readers - * and writers. Since we don't need to export full Lock API, we - * just override the minimal AQS methods and use them directly. - */ - static final class TreeBin extends AbstractQueuedSynchronizer { - private static final long serialVersionUID = 2249069246763182397L; - transient TreeNode root; // root of tree - transient TreeNode first; // head of next-pointer list - - /* AQS overrides */ - public final boolean isHeldExclusively() { return getState() > 0; } - public final boolean tryAcquire(int ignore) { - if (compareAndSetState(0, 1)) { - setExclusiveOwnerThread(Thread.currentThread()); - return true; - } - return false; - } - public final boolean tryRelease(int ignore) { - setExclusiveOwnerThread(null); - setState(0); - return true; - } - public final int tryAcquireShared(int ignore) { - for (int c;;) { - if ((c = getState()) > 0) - return -1; - if (compareAndSetState(c, c -1)) - return 1; - } - } - public final boolean tryReleaseShared(int ignore) { - int c; - do {} while (!compareAndSetState(c = getState(), c + 1)); - return c == -1; - } - - /** From CLR */ - private void rotateLeft(TreeNode p) { - if (p != null) { - TreeNode r = p.right, pp, rl; - if ((rl = p.right = r.left) != null) - rl.parent = p; - if ((pp = r.parent = p.parent) == null) - root = r; - else if (pp.left == p) - pp.left = r; - else - pp.right = r; - r.left = p; - p.parent = r; - } - } - - /** From CLR */ - private void rotateRight(TreeNode p) { - if (p != null) { - TreeNode l = p.left, pp, lr; - if ((lr = p.left = l.right) != null) - lr.parent = p; - if ((pp = l.parent = p.parent) == null) - root = l; - else if (pp.right == p) - pp.right = l; - else - pp.left = l; - l.right = p; - p.parent = l; - } - } - - @SuppressWarnings("unchecked") final TreeNode getTreeNode - (int h, Object k, TreeNode p) { - return getTreeNode(h, (RubyObject)k, p); - } - - /** - * Returns the TreeNode (or null if not found) for the given key - * starting at given root. - */ - @SuppressWarnings("unchecked") final TreeNode getTreeNode - (int h, RubyObject k, TreeNode p) { - RubyClass c = k.getMetaClass(); boolean kNotComparable = !k.respondsTo("<=>"); - while (p != null) { - int dir, ph; RubyObject pk; RubyClass pc; - if ((ph = p.hash) == h) { - if ((pk = (RubyObject)p.key) == k || k.equals(pk)) - return p; - if (c != (pc = (RubyClass)pk.getMetaClass()) || - kNotComparable || - (dir = rubyCompare(k, pk)) == 0) { - dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); - if (dir == 0) { // if still stuck, need to check both sides - TreeNode r = null, pl, pr; - // try to recurse on the right - if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) - return r; - // try to continue iterating on the left side - else if ((pl = p.left) != null && h <= pl.hash) - dir = -1; - else // no matching node found - return null; - } - } - } - else - dir = (h < ph) ? -1 : 1; - p = (dir > 0) ? p.right : p.left; - } - return null; - } - - int rubyCompare(RubyObject l, RubyObject r) { - ThreadContext context = l.getMetaClass().getRuntime().getCurrentContext(); - IRubyObject result; - try { - result = l.callMethod(context, "<=>", r); - } catch (RaiseException e) { - // handle objects "lying" about responding to <=>, ie: an Array containing non-comparable keys - if (context.runtime.getNoMethodError().isInstance(e.getException())) { - return 0; - } - throw e; - } - - return result.isNil() ? 0 : RubyNumeric.num2int(result.convertToInteger()); - } - - /** - * Wrapper for getTreeNode used by CHM.get. Tries to obtain - * read-lock to call getTreeNode, but during failure to get - * lock, searches along next links. - */ - final Object getValue(int h, Object k) { - Node r = null; - int c = getState(); // Must read lock state first - for (Node e = first; e != null; e = e.next) { - if (c <= 0 && compareAndSetState(c, c - 1)) { - try { - r = getTreeNode(h, k, root); - } finally { - releaseShared(0); - } - break; - } - else if ((e.hash & HASH_BITS) == h && k.equals(e.key)) { - r = e; - break; - } - else - c = getState(); - } - return r == null ? null : r.val; - } - - @SuppressWarnings("unchecked") final TreeNode putTreeNode - (int h, Object k, Object v) { - return putTreeNode(h, (RubyObject)k, v); - } - - /** - * Finds or adds a node. - * @return null if added - */ - @SuppressWarnings("unchecked") final TreeNode putTreeNode - (int h, RubyObject k, Object v) { - RubyClass c = k.getMetaClass(); - boolean kNotComparable = !k.respondsTo("<=>"); - TreeNode pp = root, p = null; - int dir = 0; - while (pp != null) { // find existing node or leaf to insert at - int ph; RubyObject pk; RubyClass pc; - p = pp; - if ((ph = p.hash) == h) { - if ((pk = (RubyObject)p.key) == k || k.equals(pk)) - return p; - if (c != (pc = pk.getMetaClass()) || - kNotComparable || - (dir = rubyCompare(k, pk)) == 0) { - dir = (c == pc) ? 0 : c.getName().compareTo(pc.getName()); - if (dir == 0) { // if still stuck, need to check both sides - TreeNode r = null, pr; - // try to recurse on the right - if ((pr = p.right) != null && h >= pr.hash && (r = getTreeNode(h, k, pr)) != null) - return r; - else // continue descending down the left subtree - dir = -1; - } - } - } - else - dir = (h < ph) ? -1 : 1; - pp = (dir > 0) ? p.right : p.left; - } - - TreeNode f = first; - TreeNode x = first = new TreeNode(h, (Object)k, v, f, p); - if (p == null) - root = x; - else { // attach and rebalance; adapted from CLR - TreeNode xp, xpp; - if (f != null) - f.prev = x; - if (dir <= 0) - p.left = x; - else - p.right = x; - x.red = true; - while (x != null && (xp = x.parent) != null && xp.red && - (xpp = xp.parent) != null) { - TreeNode xppl = xpp.left; - if (xp == xppl) { - TreeNode y = xpp.right; - if (y != null && y.red) { - y.red = false; - xp.red = false; - xpp.red = true; - x = xpp; - } - else { - if (x == xp.right) { - rotateLeft(x = xp); - xpp = (xp = x.parent) == null ? null : xp.parent; - } - if (xp != null) { - xp.red = false; - if (xpp != null) { - xpp.red = true; - rotateRight(xpp); - } - } - } - } - else { - TreeNode y = xppl; - if (y != null && y.red) { - y.red = false; - xp.red = false; - xpp.red = true; - x = xpp; - } - else { - if (x == xp.left) { - rotateRight(x = xp); - xpp = (xp = x.parent) == null ? null : xp.parent; - } - if (xp != null) { - xp.red = false; - if (xpp != null) { - xpp.red = true; - rotateLeft(xpp); - } - } - } - } - } - TreeNode r = root; - if (r != null && r.red) - r.red = false; - } - return null; - } - - /** - * Removes the given node, that must be present before this - * call. This is messier than typical red-black deletion code - * because we cannot swap the contents of an interior node - * with a leaf successor that is pinned by "next" pointers - * that are accessible independently of lock. So instead we - * swap the tree linkages. - */ - final void deleteTreeNode(TreeNode p) { - TreeNode next = (TreeNode)p.next; // unlink traversal pointers - TreeNode pred = p.prev; - if (pred == null) - first = next; - else - pred.next = next; - if (next != null) - next.prev = pred; - TreeNode replacement; - TreeNode pl = p.left; - TreeNode pr = p.right; - if (pl != null && pr != null) { - TreeNode s = pr, sl; - while ((sl = s.left) != null) // find successor - s = sl; - boolean c = s.red; s.red = p.red; p.red = c; // swap colors - TreeNode sr = s.right; - TreeNode pp = p.parent; - if (s == pr) { // p was s's direct parent - p.parent = s; - s.right = p; - } - else { - TreeNode sp = s.parent; - if ((p.parent = sp) != null) { - if (s == sp.left) - sp.left = p; - else - sp.right = p; - } - if ((s.right = pr) != null) - pr.parent = s; - } - p.left = null; - if ((p.right = sr) != null) - sr.parent = p; - if ((s.left = pl) != null) - pl.parent = s; - if ((s.parent = pp) == null) - root = s; - else if (p == pp.left) - pp.left = s; - else - pp.right = s; - replacement = sr; - } - else - replacement = (pl != null) ? pl : pr; - TreeNode pp = p.parent; - if (replacement == null) { - if (pp == null) { - root = null; - return; - } - replacement = p; - } - else { - replacement.parent = pp; - if (pp == null) - root = replacement; - else if (p == pp.left) - pp.left = replacement; - else - pp.right = replacement; - p.left = p.right = p.parent = null; - } - if (!p.red) { // rebalance, from CLR - TreeNode x = replacement; - while (x != null) { - TreeNode xp, xpl; - if (x.red || (xp = x.parent) == null) { - x.red = false; - break; - } - if (x == (xpl = xp.left)) { - TreeNode sib = xp.right; - if (sib != null && sib.red) { - sib.red = false; - xp.red = true; - rotateLeft(xp); - sib = (xp = x.parent) == null ? null : xp.right; - } - if (sib == null) - x = xp; - else { - TreeNode sl = sib.left, sr = sib.right; - if ((sr == null || !sr.red) && - (sl == null || !sl.red)) { - sib.red = true; - x = xp; - } - else { - if (sr == null || !sr.red) { - if (sl != null) - sl.red = false; - sib.red = true; - rotateRight(sib); - sib = (xp = x.parent) == null ? null : xp.right; - } - if (sib != null) { - sib.red = (xp == null) ? false : xp.red; - if ((sr = sib.right) != null) - sr.red = false; - } - if (xp != null) { - xp.red = false; - rotateLeft(xp); - } - x = root; - } - } - } - else { // symmetric - TreeNode sib = xpl; - if (sib != null && sib.red) { - sib.red = false; - xp.red = true; - rotateRight(xp); - sib = (xp = x.parent) == null ? null : xp.left; - } - if (sib == null) - x = xp; - else { - TreeNode sl = sib.left, sr = sib.right; - if ((sl == null || !sl.red) && - (sr == null || !sr.red)) { - sib.red = true; - x = xp; - } - else { - if (sl == null || !sl.red) { - if (sr != null) - sr.red = false; - sib.red = true; - rotateLeft(sib); - sib = (xp = x.parent) == null ? null : xp.left; - } - if (sib != null) { - sib.red = (xp == null) ? false : xp.red; - if ((sl = sib.left) != null) - sl.red = false; - } - if (xp != null) { - xp.red = false; - rotateRight(xp); - } - x = root; - } - } - } - } - } - if (p == replacement && (pp = p.parent) != null) { - if (p == pp.left) // detach pointers - pp.left = null; - else if (p == pp.right) - pp.right = null; - p.parent = null; - } - } - } - - /* ---------------- Collision reduction methods -------------- */ - - /** - * Spreads higher bits to lower, and also forces top 2 bits to 0. - * Because the table uses power-of-two masking, sets of hashes - * that vary only in bits above the current mask will always - * collide. (Among known examples are sets of Float keys holding - * consecutive whole numbers in small tables.) To counter this, - * we apply a transform that spreads the impact of higher bits - * downward. There is a tradeoff between speed, utility, and - * quality of bit-spreading. Because many common sets of hashes - * are already reasonably distributed across bits (so don't benefit - * from spreading), and because we use trees to handle large sets - * of collisions in bins, we don't need excessively high quality. - */ - private static final int spread(int h) { - h ^= (h >>> 18) ^ (h >>> 12); - return (h ^ (h >>> 10)) & HASH_BITS; - } - - /** - * Replaces a list bin with a tree bin. Call only when locked. - * Fails to replace if the given key is non-comparable or table - * is, or needs, resizing. - */ - private final void replaceWithTreeBin(AtomicReferenceArray tab, int index, Object key) { - if ((key instanceof Comparable) && - (tab.length() >= MAXIMUM_CAPACITY || counter.sum() < (long)sizeCtl)) { - TreeBin t = new TreeBin(); - for (Node e = tabAt(tab, index); e != null; e = e.next) - t.putTreeNode(e.hash & HASH_BITS, e.key, e.val); - setTabAt(tab, index, new Node(MOVED, t, null, null)); - } - } - - /* ---------------- Internal access and update methods -------------- */ - - /** Implementation for get and containsKey */ - private final Object internalGet(Object k) { - int h = spread(k.hashCode()); - retry: for (AtomicReferenceArray tab = table; tab != null;) { - Node e, p; Object ek, ev; int eh; // locals to read fields once - for (e = tabAt(tab, (tab.length() - 1) & h); e != null; e = e.next) { - if ((eh = e.hash) == MOVED) { - if ((ek = e.key) instanceof TreeBin) // search TreeBin - return ((TreeBin)ek).getValue(h, k); - else { // restart with new table - tab = (AtomicReferenceArray)ek; - continue retry; - } - } - else if ((eh & HASH_BITS) == h && (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) - return ev; - } - break; - } - return null; - } - - /** - * Implementation for the four public remove/replace methods: - * Replaces node value with v, conditional upon match of cv if - * non-null. If resulting value is null, delete. - */ - private final Object internalReplace(Object k, Object v, Object cv) { - int h = spread(k.hashCode()); - Object oldVal = null; - for (AtomicReferenceArray tab = table;;) { - Node f; int i, fh; Object fk; - if (tab == null || - (f = tabAt(tab, i = (tab.length() - 1) & h)) == null) - break; - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean validated = false; - boolean deleted = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - validated = true; - TreeNode p = t.getTreeNode(h, k, t.root); - if (p != null) { - Object pv = p.val; - if (cv == null || cv == pv || cv.equals(pv)) { - oldVal = pv; - if ((p.val = v) == null) { - deleted = true; - t.deleteTreeNode(p); - } - } - } - } - } finally { - t.release(0); - } - if (validated) { - if (deleted) - counter.add(-1L); - break; - } - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & HASH_BITS) != h && f.next == null) // precheck - break; // rules out possible existence - else if ((fh & LOCKED) != 0) { - checkForResize(); // try resizing if can't get lock - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - boolean validated = false; - boolean deleted = false; - try { - if (tabAt(tab, i) == f) { - validated = true; - for (Node e = f, pred = null;;) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - ((ev = e.val) != null) && - ((ek = e.key) == k || k.equals(ek))) { - if (cv == null || cv == ev || cv.equals(ev)) { - oldVal = ev; - if ((e.val = v) == null) { - deleted = true; - Node en = e.next; - if (pred != null) - pred.next = en; - else - setTabAt(tab, i, en); - } - } - break; - } - pred = e; - if ((e = e.next) == null) - break; - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (validated) { - if (deleted) - counter.add(-1L); - break; - } - } - } - return oldVal; - } - - /* - * Internal versions of the six insertion methods, each a - * little more complicated than the last. All have - * the same basic structure as the first (internalPut): - * 1. If table uninitialized, create - * 2. If bin empty, try to CAS new node - * 3. If bin stale, use new table - * 4. if bin converted to TreeBin, validate and relay to TreeBin methods - * 5. Lock and validate; if valid, scan and add or update - * - * The others interweave other checks and/or alternative actions: - * * Plain put checks for and performs resize after insertion. - * * putIfAbsent prescans for mapping without lock (and fails to add - * if present), which also makes pre-emptive resize checks worthwhile. - * * computeIfAbsent extends form used in putIfAbsent with additional - * mechanics to deal with, calls, potential exceptions and null - * returns from function call. - * * compute uses the same function-call mechanics, but without - * the prescans - * * merge acts as putIfAbsent in the absent case, but invokes the - * update function if present - * * putAll attempts to pre-allocate enough table space - * and more lazily performs count updates and checks. - * - * Someday when details settle down a bit more, it might be worth - * some factoring to reduce sprawl. - */ - - /** Implementation for put */ - private final Object internalPut(Object k, Object v) { - int h = spread(k.hashCode()); - int count = 0; - for (AtomicReferenceArray tab = table;;) { - int i; Node f; int fh; Object fk; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { - if (casTabAt(tab, i, null, new Node(h, k, v, null))) - break; // no lock when adding to empty bin - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - Object oldVal = null; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 2; - TreeNode p = t.putTreeNode(h, k, v); - if (p != null) { - oldVal = p.val; - p.val = v; - } - } - } finally { - t.release(0); - } - if (count != 0) { - if (oldVal != null) - return oldVal; - break; - } - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - Object oldVal = null; - try { // needed in case equals() throws - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - oldVal = ev; - e.val = v; - break; - } - Node last = e; - if ((e = e.next) == null) { - last.next = new Node(h, k, v, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { // unlock and signal if needed - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (oldVal != null) - return oldVal; - if (tab.length() <= 64) - count = 2; - break; - } - } - } - counter.add(1L); - if (count > 1) - checkForResize(); - return null; - } - - /** Implementation for putIfAbsent */ - private final Object internalPutIfAbsent(Object k, Object v) { - int h = spread(k.hashCode()); - int count = 0; - for (AtomicReferenceArray tab = table;;) { - int i; Node f; int fh; Object fk, fv; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { - if (casTabAt(tab, i, null, new Node(h, k, v, null))) - break; - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - Object oldVal = null; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 2; - TreeNode p = t.putTreeNode(h, k, v); - if (p != null) - oldVal = p.val; - } - } finally { - t.release(0); - } - if (count != 0) { - if (oldVal != null) - return oldVal; - break; - } - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & HASH_BITS) == h && (fv = f.val) != null && - ((fk = f.key) == k || k.equals(fk))) - return fv; - else { - Node g = f.next; - if (g != null) { // at least 2 nodes -- search and maybe resize - for (Node e = g;;) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) - return ev; - if ((e = e.next) == null) { - checkForResize(); - break; - } - } - } - if (((fh = f.hash) & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { - Object oldVal = null; - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - oldVal = ev; - break; - } - Node last = e; - if ((e = e.next) == null) { - last.next = new Node(h, k, v, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (oldVal != null) - return oldVal; - if (tab.length() <= 64) - count = 2; - break; - } - } - } - } - counter.add(1L); - if (count > 1) - checkForResize(); - return null; - } - - /** Implementation for computeIfAbsent */ - private final Object internalComputeIfAbsent(K k, - Fun mf) { - int h = spread(k.hashCode()); - Object val = null; - int count = 0; - for (AtomicReferenceArray tab = table;;) { - Node f; int i, fh; Object fk, fv; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { - Node node = new Node(fh = h | LOCKED, k, null, null); - if (casTabAt(tab, i, null, node)) { - count = 1; - try { - if ((val = mf.apply(k)) != null) - node.val = val; - } finally { - if (val == null) - setTabAt(tab, i, null); - if (!node.casHash(fh, h)) { - node.hash = h; - synchronized (node) { node.notifyAll(); }; - } - } - } - if (count != 0) - break; - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean added = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 1; - TreeNode p = t.getTreeNode(h, k, t.root); - if (p != null) - val = p.val; - else if ((val = mf.apply(k)) != null) { - added = true; - count = 2; - t.putTreeNode(h, k, val); - } - } - } finally { - t.release(0); - } - if (count != 0) { - if (!added) - return val; - break; - } - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & HASH_BITS) == h && (fv = f.val) != null && - ((fk = f.key) == k || k.equals(fk))) - return fv; - else { - Node g = f.next; - if (g != null) { - for (Node e = g;;) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) - return ev; - if ((e = e.next) == null) { - checkForResize(); - break; - } - } - } - if (((fh = f.hash) & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (tabAt(tab, i) == f && f.casHash(fh, fh | LOCKED)) { - boolean added = false; - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - val = ev; - break; - } - Node last = e; - if ((e = e.next) == null) { - if ((val = mf.apply(k)) != null) { - added = true; - last.next = new Node(h, k, val, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - } - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (!added) - return val; - if (tab.length() <= 64) - count = 2; - break; - } - } - } - } - if (val != null) { - counter.add(1L); - if (count > 1) - checkForResize(); - } - return val; - } - - /** Implementation for compute */ - @SuppressWarnings("unchecked") private final Object internalCompute - (K k, boolean onlyIfPresent, BiFun mf) { - int h = spread(k.hashCode()); - Object val = null; - int delta = 0; - int count = 0; - for (AtomicReferenceArray tab = table;;) { - Node f; int i, fh; Object fk; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { - if (onlyIfPresent) - break; - Node node = new Node(fh = h | LOCKED, k, null, null); - if (casTabAt(tab, i, null, node)) { - try { - count = 1; - if ((val = mf.apply(k, null)) != null) { - node.val = val; - delta = 1; - } - } finally { - if (delta == 0) - setTabAt(tab, i, null); - if (!node.casHash(fh, h)) { - node.hash = h; - synchronized (node) { node.notifyAll(); }; - } - } - } - if (count != 0) - break; - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 1; - TreeNode p = t.getTreeNode(h, k, t.root); - Object pv; - if (p == null) { - if (onlyIfPresent) - break; - pv = null; - } else - pv = p.val; - if ((val = mf.apply(k, (V)pv)) != null) { - if (p != null) - p.val = val; - else { - count = 2; - delta = 1; - t.putTreeNode(h, k, val); - } - } - else if (p != null) { - delta = -1; - t.deleteTreeNode(p); - } - } - } finally { - t.release(0); - } - if (count != 0) - break; - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f, pred = null;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - val = mf.apply(k, (V)ev); - if (val != null) - e.val = val; - else { - delta = -1; - Node en = e.next; - if (pred != null) - pred.next = en; - else - setTabAt(tab, i, en); - } - break; - } - pred = e; - if ((e = e.next) == null) { - if (!onlyIfPresent && (val = mf.apply(k, null)) != null) { - pred.next = new Node(h, k, val, null); - delta = 1; - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - } - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (tab.length() <= 64) - count = 2; - break; - } - } - } - if (delta != 0) { - counter.add((long)delta); - if (count > 1) - checkForResize(); - } - return val; - } - - /** Implementation for merge */ - @SuppressWarnings("unchecked") private final Object internalMerge - (K k, V v, BiFun mf) { - int h = spread(k.hashCode()); - Object val = null; - int delta = 0; - int count = 0; - for (AtomicReferenceArray tab = table;;) { - int i; Node f; int fh; Object fk, fv; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null) { - if (casTabAt(tab, i, null, new Node(h, k, v, null))) { - delta = 1; - val = v; - break; - } - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - count = 1; - TreeNode p = t.getTreeNode(h, k, t.root); - val = (p == null) ? v : mf.apply((V)p.val, v); - if (val != null) { - if (p != null) - p.val = val; - else { - count = 2; - delta = 1; - t.putTreeNode(h, k, val); - } - } - else if (p != null) { - delta = -1; - t.deleteTreeNode(p); - } - } - } finally { - t.release(0); - } - if (count != 0) - break; - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & LOCKED) != 0) { - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f, pred = null;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - val = mf.apply((V)ev, v); - if (val != null) - e.val = val; - else { - delta = -1; - Node en = e.next; - if (pred != null) - pred.next = en; - else - setTabAt(tab, i, en); - } - break; - } - pred = e; - if ((e = e.next) == null) { - val = v; - pred.next = new Node(h, k, val, null); - delta = 1; - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (tab.length() <= 64) - count = 2; - break; - } - } - } - if (delta != 0) { - counter.add((long)delta); - if (count > 1) - checkForResize(); - } - return val; - } - - /** Implementation for putAll */ - private final void internalPutAll(Map m) { - tryPresize(m.size()); - long delta = 0L; // number of uncommitted additions - boolean npe = false; // to throw exception on exit for nulls - try { // to clean up counts on other exceptions - for (Map.Entry entry : m.entrySet()) { - Object k, v; - if (entry == null || (k = entry.getKey()) == null || - (v = entry.getValue()) == null) { - npe = true; - break; - } - int h = spread(k.hashCode()); - for (AtomicReferenceArray tab = table;;) { - int i; Node f; int fh; Object fk; - if (tab == null) - tab = initTable(); - else if ((f = tabAt(tab, i = (tab.length() - 1) & h)) == null){ - if (casTabAt(tab, i, null, new Node(h, k, v, null))) { - ++delta; - break; - } - } - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean validated = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - validated = true; - TreeNode p = t.getTreeNode(h, k, t.root); - if (p != null) - p.val = v; - else { - t.putTreeNode(h, k, v); - ++delta; - } - } - } finally { - t.release(0); - } - if (validated) - break; - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & LOCKED) != 0) { - counter.add(delta); - delta = 0L; - checkForResize(); - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - int count = 0; - try { - if (tabAt(tab, i) == f) { - count = 1; - for (Node e = f;; ++count) { - Object ek, ev; - if ((e.hash & HASH_BITS) == h && - (ev = e.val) != null && - ((ek = e.key) == k || k.equals(ek))) { - e.val = v; - break; - } - Node last = e; - if ((e = e.next) == null) { - ++delta; - last.next = new Node(h, k, v, null); - if (count >= TREE_THRESHOLD) - replaceWithTreeBin(tab, i, k); - break; - } - } - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (count != 0) { - if (count > 1) { - counter.add(delta); - delta = 0L; - checkForResize(); - } - break; - } - } - } - } - } finally { - if (delta != 0) - counter.add(delta); - } - if (npe) - throw new NullPointerException(); - } - - /* ---------------- Table Initialization and Resizing -------------- */ - - /** - * Returns a power of two table size for the given desired capacity. - * See Hackers Delight, sec 3.2 - */ - private static final int tableSizeFor(int c) { - int n = c - 1; - n |= n >>> 1; - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; - } - - /** - * Initializes table, using the size recorded in sizeCtl. - */ - private final AtomicReferenceArray initTable() { - AtomicReferenceArray tab; int sc; - while ((tab = table) == null) { - if ((sc = sizeCtl) < 0) - Thread.yield(); // lost initialization race; just spin - else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { - try { - if ((tab = table) == null) { - int n = (sc > 0) ? sc : DEFAULT_CAPACITY; - tab = table = new AtomicReferenceArray(n); - sc = n - (n >>> 2); - } - } finally { - sizeCtl = sc; - } - break; - } - } - return tab; - } - - /** - * If table is too small and not already resizing, creates next - * table and transfers bins. Rechecks occupancy after a transfer - * to see if another resize is already needed because resizings - * are lagging additions. - */ - private final void checkForResize() { - AtomicReferenceArray tab; int n, sc; - while ((tab = table) != null && - (n = tab.length()) < MAXIMUM_CAPACITY && - (sc = sizeCtl) >= 0 && counter.sum() >= (long)sc && - SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { - try { - if (tab == table) { - table = rebuild(tab); - sc = (n << 1) - (n >>> 1); - } - } finally { - sizeCtl = sc; - } - } - } - - /** - * Tries to presize table to accommodate the given number of elements. - * - * @param size number of elements (doesn't need to be perfectly accurate) - */ - private final void tryPresize(int size) { - int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : - tableSizeFor(size + (size >>> 1) + 1); - int sc; - while ((sc = sizeCtl) >= 0) { - AtomicReferenceArray tab = table; int n; - if (tab == null || (n = tab.length()) == 0) { - n = (sc > c) ? sc : c; - if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { - try { - if (table == tab) { - table = new AtomicReferenceArray(n); - sc = n - (n >>> 2); - } - } finally { - sizeCtl = sc; - } - } - } - else if (c <= sc || n >= MAXIMUM_CAPACITY) - break; - else if (SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { - try { - if (table == tab) { - table = rebuild(tab); - sc = (n << 1) - (n >>> 1); - } - } finally { - sizeCtl = sc; - } - } - } - } - - /* - * Moves and/or copies the nodes in each bin to new table. See - * above for explanation. - * - * @return the new table - */ - private static final AtomicReferenceArray rebuild(AtomicReferenceArray tab) { - int n = tab.length(); - AtomicReferenceArray nextTab = new AtomicReferenceArray(n << 1); - Node fwd = new Node(MOVED, nextTab, null, null); - int[] buffer = null; // holds bins to revisit; null until needed - Node rev = null; // reverse forwarder; null until needed - int nbuffered = 0; // the number of bins in buffer list - int bufferIndex = 0; // buffer index of current buffered bin - int bin = n - 1; // current non-buffered bin or -1 if none - - for (int i = bin;;) { // start upwards sweep - int fh; Node f; - if ((f = tabAt(tab, i)) == null) { - if (bin >= 0) { // Unbuffered; no lock needed (or available) - if (!casTabAt(tab, i, f, fwd)) - continue; - } - else { // transiently use a locked forwarding node - Node g = new Node(MOVED|LOCKED, nextTab, null, null); - if (!casTabAt(tab, i, f, g)) - continue; - setTabAt(nextTab, i, null); - setTabAt(nextTab, i + n, null); - setTabAt(tab, i, fwd); - if (!g.casHash(MOVED|LOCKED, MOVED)) { - g.hash = MOVED; - synchronized (g) { g.notifyAll(); } - } - } - } - else if ((fh = f.hash) == MOVED) { - Object fk = f.key; - if (fk instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - boolean validated = false; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - validated = true; - splitTreeBin(nextTab, i, t); - setTabAt(tab, i, fwd); - } - } finally { - t.release(0); - } - if (!validated) - continue; - } - } - else if ((fh & LOCKED) == 0 && f.casHash(fh, fh|LOCKED)) { - boolean validated = false; - try { // split to lo and hi lists; copying as needed - if (tabAt(tab, i) == f) { - validated = true; - splitBin(nextTab, i, f); - setTabAt(tab, i, fwd); - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - if (!validated) - continue; - } - else { - if (buffer == null) // initialize buffer for revisits - buffer = new int[TRANSFER_BUFFER_SIZE]; - if (bin < 0 && bufferIndex > 0) { - int j = buffer[--bufferIndex]; - buffer[bufferIndex] = i; - i = j; // swap with another bin - continue; - } - if (bin < 0 || nbuffered >= TRANSFER_BUFFER_SIZE) { - f.tryAwaitLock(tab, i); - continue; // no other options -- block - } - if (rev == null) // initialize reverse-forwarder - rev = new Node(MOVED, tab, null, null); - if (tabAt(tab, i) != f || (f.hash & LOCKED) == 0) - continue; // recheck before adding to list - buffer[nbuffered++] = i; - setTabAt(nextTab, i, rev); // install place-holders - setTabAt(nextTab, i + n, rev); - } - - if (bin > 0) - i = --bin; - else if (buffer != null && nbuffered > 0) { - bin = -1; - i = buffer[bufferIndex = --nbuffered]; - } - else - return nextTab; - } - } - - /** - * Splits a normal bin with list headed by e into lo and hi parts; - * installs in given table. - */ - private static void splitBin(AtomicReferenceArray nextTab, int i, Node e) { - int bit = nextTab.length() >>> 1; // bit to split on - int runBit = e.hash & bit; - Node lastRun = e, lo = null, hi = null; - for (Node p = e.next; p != null; p = p.next) { - int b = p.hash & bit; - if (b != runBit) { - runBit = b; - lastRun = p; - } - } - if (runBit == 0) - lo = lastRun; - else - hi = lastRun; - for (Node p = e; p != lastRun; p = p.next) { - int ph = p.hash & HASH_BITS; - Object pk = p.key, pv = p.val; - if ((ph & bit) == 0) - lo = new Node(ph, pk, pv, lo); - else - hi = new Node(ph, pk, pv, hi); - } - setTabAt(nextTab, i, lo); - setTabAt(nextTab, i + bit, hi); - } - - /** - * Splits a tree bin into lo and hi parts; installs in given table. - */ - private static void splitTreeBin(AtomicReferenceArray nextTab, int i, TreeBin t) { - int bit = nextTab.length() >>> 1; - TreeBin lt = new TreeBin(); - TreeBin ht = new TreeBin(); - int lc = 0, hc = 0; - for (Node e = t.first; e != null; e = e.next) { - int h = e.hash & HASH_BITS; - Object k = e.key, v = e.val; - if ((h & bit) == 0) { - ++lc; - lt.putTreeNode(h, k, v); - } - else { - ++hc; - ht.putTreeNode(h, k, v); - } - } - Node ln, hn; // throw away trees if too small - if (lc <= (TREE_THRESHOLD >>> 1)) { - ln = null; - for (Node p = lt.first; p != null; p = p.next) - ln = new Node(p.hash, p.key, p.val, ln); - } - else - ln = new Node(MOVED, lt, null, null); - setTabAt(nextTab, i, ln); - if (hc <= (TREE_THRESHOLD >>> 1)) { - hn = null; - for (Node p = ht.first; p != null; p = p.next) - hn = new Node(p.hash, p.key, p.val, hn); - } - else - hn = new Node(MOVED, ht, null, null); - setTabAt(nextTab, i + bit, hn); - } - - /** - * Implementation for clear. Steps through each bin, removing all - * nodes. - */ - private final void internalClear() { - long delta = 0L; // negative number of deletions - int i = 0; - AtomicReferenceArray tab = table; - while (tab != null && i < tab.length()) { - int fh; Object fk; - Node f = tabAt(tab, i); - if (f == null) - ++i; - else if ((fh = f.hash) == MOVED) { - if ((fk = f.key) instanceof TreeBin) { - TreeBin t = (TreeBin)fk; - t.acquire(0); - try { - if (tabAt(tab, i) == f) { - for (Node p = t.first; p != null; p = p.next) { - if (p.val != null) { // (currently always true) - p.val = null; - --delta; - } - } - t.first = null; - t.root = null; - ++i; - } - } finally { - t.release(0); - } - } - else - tab = (AtomicReferenceArray)fk; - } - else if ((fh & LOCKED) != 0) { - counter.add(delta); // opportunistically update count - delta = 0L; - f.tryAwaitLock(tab, i); - } - else if (f.casHash(fh, fh | LOCKED)) { - try { - if (tabAt(tab, i) == f) { - for (Node e = f; e != null; e = e.next) { - if (e.val != null) { // (currently always true) - e.val = null; - --delta; - } - } - setTabAt(tab, i, null); - ++i; - } - } finally { - if (!f.casHash(fh | LOCKED, fh)) { - f.hash = fh; - synchronized (f) { f.notifyAll(); }; - } - } - } - } - if (delta != 0) - counter.add(delta); - } - - /* ----------------Table Traversal -------------- */ - - /** - * Encapsulates traversal for methods such as containsValue; also - * serves as a base class for other iterators and bulk tasks. - * - * At each step, the iterator snapshots the key ("nextKey") and - * value ("nextVal") of a valid node (i.e., one that, at point of - * snapshot, has a non-null user value). Because val fields can - * change (including to null, indicating deletion), field nextVal - * might not be accurate at point of use, but still maintains the - * weak consistency property of holding a value that was once - * valid. To support iterator.remove, the nextKey field is not - * updated (nulled out) when the iterator cannot advance. - * - * Internal traversals directly access these fields, as in: - * {@code while (it.advance() != null) { process(it.nextKey); }} - * - * Exported iterators must track whether the iterator has advanced - * (in hasNext vs next) (by setting/checking/nulling field - * nextVal), and then extract key, value, or key-value pairs as - * return values of next(). - * - * The iterator visits once each still-valid node that was - * reachable upon iterator construction. It might miss some that - * were added to a bin after the bin was visited, which is OK wrt - * consistency guarantees. Maintaining this property in the face - * of possible ongoing resizes requires a fair amount of - * bookkeeping state that is difficult to optimize away amidst - * volatile accesses. Even so, traversal maintains reasonable - * throughput. - * - * Normally, iteration proceeds bin-by-bin traversing lists. - * However, if the table has been resized, then all future steps - * must traverse both the bin at the current index as well as at - * (index + baseSize); and so on for further resizings. To - * paranoically cope with potential sharing by users of iterators - * across threads, iteration terminates if a bounds checks fails - * for a table read. - * - * This class extends ForkJoinTask to streamline parallel - * iteration in bulk operations (see BulkTask). This adds only an - * int of space overhead, which is close enough to negligible in - * cases where it is not needed to not worry about it. Because - * ForkJoinTask is Serializable, but iterators need not be, we - * need to add warning suppressions. - */ - @SuppressWarnings("serial") static class Traverser { - final ConcurrentHashMapV8 map; - Node next; // the next entry to use - K nextKey; // cached key field of next - V nextVal; // cached val field of next - AtomicReferenceArray tab; // current table; updated if resized - int index; // index of bin to use next - int baseIndex; // current index of initial table - int baseLimit; // index bound for initial table - int baseSize; // initial table size - - /** Creates iterator for all entries in the table. */ - Traverser(ConcurrentHashMapV8 map) { - this.map = map; - } - - /** Creates iterator for split() methods */ - Traverser(Traverser it) { - ConcurrentHashMapV8 m; AtomicReferenceArray t; - if ((m = this.map = it.map) == null) - t = null; - else if ((t = it.tab) == null && // force parent tab initialization - (t = it.tab = m.table) != null) - it.baseLimit = it.baseSize = t.length(); - this.tab = t; - this.baseSize = it.baseSize; - it.baseLimit = this.index = this.baseIndex = - ((this.baseLimit = it.baseLimit) + it.baseIndex + 1) >>> 1; - } - - /** - * Advances next; returns nextVal or null if terminated. - * See above for explanation. - */ - final V advance() { - Node e = next; - V ev = null; - outer: do { - if (e != null) // advance past used/skipped node - e = e.next; - while (e == null) { // get to next non-null bin - ConcurrentHashMapV8 m; - AtomicReferenceArray t; int b, i, n; Object ek; // checks must use locals - if ((t = tab) != null) - n = t.length(); - else if ((m = map) != null && (t = tab = m.table) != null) - n = baseLimit = baseSize = t.length(); - else - break outer; - if ((b = baseIndex) >= baseLimit || - (i = index) < 0 || i >= n) - break outer; - if ((e = tabAt(t, i)) != null && e.hash == MOVED) { - if ((ek = e.key) instanceof TreeBin) - e = ((TreeBin)ek).first; - else { - tab = (AtomicReferenceArray)ek; - continue; // restarts due to null val - } - } // visit upper slots if present - index = (i += baseSize) < n ? i : (baseIndex = b + 1); - } - nextKey = (K) e.key; - } while ((ev = (V) e.val) == null); // skip deleted or special nodes - next = e; - return nextVal = ev; - } - - public final void remove() { - Object k = nextKey; - if (k == null && (advance() == null || (k = nextKey) == null)) - throw new IllegalStateException(); - map.internalReplace(k, null, null); - } - - public final boolean hasNext() { - return nextVal != null || advance() != null; - } - - public final boolean hasMoreElements() { return hasNext(); } - public final void setRawResult(Object x) { } - public R getRawResult() { return null; } - public boolean exec() { return true; } - } - - /* ---------------- Public operations -------------- */ - - /** - * Creates a new, empty map with the default initial table size (16). - */ - public ConcurrentHashMapV8() { - this.counter = new LongAdder(); - } - - /** - * Creates a new, empty map with an initial table size - * accommodating the specified number of elements without the need - * to dynamically resize. - * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. - * @throws IllegalArgumentException if the initial capacity of - * elements is negative - */ - public ConcurrentHashMapV8(int initialCapacity) { - if (initialCapacity < 0) - throw new IllegalArgumentException(); - int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? - MAXIMUM_CAPACITY : - tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); - this.counter = new LongAdder(); - this.sizeCtl = cap; - } - - /** - * Creates a new map with the same mappings as the given map. - * - * @param m the map - */ - public ConcurrentHashMapV8(Map m) { - this.counter = new LongAdder(); - this.sizeCtl = DEFAULT_CAPACITY; - internalPutAll(m); - } - - /** - * Creates a new, empty map with an initial table size based on - * the given number of elements ({@code initialCapacity}) and - * initial table density ({@code loadFactor}). - * - * @param initialCapacity the initial capacity. The implementation - * performs internal sizing to accommodate this many elements, - * given the specified load factor. - * @param loadFactor the load factor (table density) for - * establishing the initial table size - * @throws IllegalArgumentException if the initial capacity of - * elements is negative or the load factor is nonpositive - * - * @since 1.6 - */ - public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { - this(initialCapacity, loadFactor, 1); - } - - /** - * Creates a new, empty map with an initial table size based on - * the given number of elements ({@code initialCapacity}), table - * density ({@code loadFactor}), and number of concurrently - * updating threads ({@code concurrencyLevel}). - * - * @param initialCapacity the initial capacity. The implementation - * performs internal sizing to accommodate this many elements, - * given the specified load factor. - * @param loadFactor the load factor (table density) for - * establishing the initial table size - * @param concurrencyLevel the estimated number of concurrently - * updating threads. The implementation may use this value as - * a sizing hint. - * @throws IllegalArgumentException if the initial capacity is - * negative or the load factor or concurrencyLevel are - * nonpositive - */ - public ConcurrentHashMapV8(int initialCapacity, - float loadFactor, int concurrencyLevel) { - if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) - throw new IllegalArgumentException(); - if (initialCapacity < concurrencyLevel) // Use at least as many bins - initialCapacity = concurrencyLevel; // as estimated threads - long size = (long)(1.0 + (long)initialCapacity / loadFactor); - int cap = (size >= (long)MAXIMUM_CAPACITY) ? - MAXIMUM_CAPACITY : tableSizeFor((int)size); - this.counter = new LongAdder(); - this.sizeCtl = cap; - } - - /** - * Creates a new {@link Set} backed by a ConcurrentHashMapV8 - * from the given type to {@code Boolean.TRUE}. - * - * @return the new set - */ - public static KeySetView newKeySet() { - return new KeySetView(new ConcurrentHashMapV8(), - Boolean.TRUE); - } - - /** - * Creates a new {@link Set} backed by a ConcurrentHashMapV8 - * from the given type to {@code Boolean.TRUE}. - * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. - * @throws IllegalArgumentException if the initial capacity of - * elements is negative - * @return the new set - */ - public static KeySetView newKeySet(int initialCapacity) { - return new KeySetView(new ConcurrentHashMapV8(initialCapacity), - Boolean.TRUE); - } - - /** - * {@inheritDoc} - */ - public boolean isEmpty() { - return counter.sum() <= 0L; // ignore transient negative values - } - - /** - * {@inheritDoc} - */ - public int size() { - long n = counter.sum(); - return ((n < 0L) ? 0 : - (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : - (int)n); - } - - /** - * Returns the number of mappings. This method should be used - * instead of {@link #size} because a ConcurrentHashMapV8 may - * contain more mappings than can be represented as an int. The - * value returned is a snapshot; the actual count may differ if - * there are ongoing concurrent insertions or removals. - * - * @return the number of mappings - */ - public long mappingCount() { - long n = counter.sum(); - return (n < 0L) ? 0L : n; // ignore transient negative values - } - - /** - * Returns the value to which the specified key is mapped, - * or {@code null} if this map contains no mapping for the key. - * - *

More formally, if this map contains a mapping from a key - * {@code k} to a value {@code v} such that {@code key.equals(k)}, - * then this method returns {@code v}; otherwise it returns - * {@code null}. (There can be at most one such mapping.) - * - * @throws NullPointerException if the specified key is null - */ - @SuppressWarnings("unchecked") public V get(Object key) { - if (key == null) - throw new NullPointerException(); - return (V)internalGet(key); - } - - /** - * Returns the value to which the specified key is mapped, - * or the given defaultValue if this map contains no mapping for the key. - * - * @param key the key - * @param defaultValue the value to return if this map contains - * no mapping for the given key - * @return the mapping for the key, if present; else the defaultValue - * @throws NullPointerException if the specified key is null - */ - @SuppressWarnings("unchecked") public V getValueOrDefault(Object key, V defaultValue) { - if (key == null) - throw new NullPointerException(); - V v = (V) internalGet(key); - return v == null ? defaultValue : v; - } - - /** - * Tests if the specified object is a key in this table. - * - * @param key possible key - * @return {@code true} if and only if the specified object - * is a key in this table, as determined by the - * {@code equals} method; {@code false} otherwise - * @throws NullPointerException if the specified key is null - */ - public boolean containsKey(Object key) { - if (key == null) - throw new NullPointerException(); - return internalGet(key) != null; - } - - /** - * Returns {@code true} if this map maps one or more keys to the - * specified value. Note: This method may require a full traversal - * of the map, and is much slower than method {@code containsKey}. - * - * @param value value whose presence in this map is to be tested - * @return {@code true} if this map maps one or more keys to the - * specified value - * @throws NullPointerException if the specified value is null - */ - public boolean containsValue(Object value) { - if (value == null) - throw new NullPointerException(); - Object v; - Traverser it = new Traverser(this); - while ((v = it.advance()) != null) { - if (v == value || value.equals(v)) - return true; - } - return false; - } - - public K findKey(Object value) { - if (value == null) - throw new NullPointerException(); - Object v; - Traverser it = new Traverser(this); - while ((v = it.advance()) != null) { - if (v == value || value.equals(v)) - return it.nextKey; - } - return null; - } - - /** - * Legacy method testing if some key maps into the specified value - * in this table. This method is identical in functionality to - * {@link #containsValue}, and exists solely to ensure - * full compatibility with class {@link java.util.Hashtable}, - * which supported this method prior to introduction of the - * Java Collections framework. - * - * @param value a value to search for - * @return {@code true} if and only if some key maps to the - * {@code value} argument in this table as - * determined by the {@code equals} method; - * {@code false} otherwise - * @throws NullPointerException if the specified value is null - */ - public boolean contains(Object value) { - return containsValue(value); - } - - /** - * Maps the specified key to the specified value in this table. - * Neither the key nor the value can be null. - * - *

The value can be retrieved by calling the {@code get} method - * with a key that is equal to the original key. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with {@code key}, or - * {@code null} if there was no mapping for {@code key} - * @throws NullPointerException if the specified key or value is null - */ - @SuppressWarnings("unchecked") public V put(K key, V value) { - if (key == null || value == null) - throw new NullPointerException(); - return (V)internalPut(key, value); - } - - /** - * {@inheritDoc} - * - * @return the previous value associated with the specified key, - * or {@code null} if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null - */ - @SuppressWarnings("unchecked") public V putIfAbsent(K key, V value) { - if (key == null || value == null) - throw new NullPointerException(); - return (V)internalPutIfAbsent(key, value); - } - - /** - * Copies all of the mappings from the specified map to this one. - * These mappings replace any mappings that this map had for any of the - * keys currently in the specified map. - * - * @param m mappings to be stored in this map - */ - public void putAll(Map m) { - internalPutAll(m); - } - - /** - * If the specified key is not already associated with a value, - * computes its value using the given mappingFunction and enters - * it into the map unless null. This is equivalent to - *

 {@code
-     * if (map.containsKey(key))
-     *   return map.get(key);
-     * value = mappingFunction.apply(key);
-     * if (value != null)
-     *   map.put(key, value);
-     * return value;}
- * - * except that the action is performed atomically. If the - * function returns {@code null} no mapping is recorded. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and no mapping is recorded. Some - * attempted update operations on this map by other threads may be - * blocked while computation is in progress, so the computation - * should be short and simple, and must not attempt to update any - * other mappings of this Map. The most appropriate usage is to - * construct a new object serving as an initial mapped value, or - * memoized result, as in: - * - *
 {@code
-     * map.computeIfAbsent(key, new Fun() {
-     *   public V map(K k) { return new Value(f(k)); }});}
- * - * @param key key with which the specified value is to be associated - * @param mappingFunction the function to compute a value - * @return the current (existing or computed) value associated with - * the specified key, or null if the computed value is null - * @throws NullPointerException if the specified key or mappingFunction - * is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map that would - * otherwise never complete - * @throws RuntimeException or Error if the mappingFunction does so, - * in which case the mapping is left unestablished - */ - @SuppressWarnings("unchecked") public V computeIfAbsent - (K key, Fun mappingFunction) { - if (key == null || mappingFunction == null) - throw new NullPointerException(); - return (V)internalComputeIfAbsent(key, mappingFunction); - } - - /** - * If the given key is present, computes a new mapping value given a key and - * its current mapped value. This is equivalent to - *
 {@code
-     *   if (map.containsKey(key)) {
-     *     value = remappingFunction.apply(key, map.get(key));
-     *     if (value != null)
-     *       map.put(key, value);
-     *     else
-     *       map.remove(key);
-     *   }
-     * }
- * - * except that the action is performed atomically. If the - * function returns {@code null}, the mapping is removed. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and the current mapping is left - * unchanged. Some attempted update operations on this map by - * other threads may be blocked while computation is in progress, - * so the computation should be short and simple, and must not - * attempt to update any other mappings of this Map. For example, - * to either create or append new messages to a value mapping: - * - * @param key key with which the specified value is to be associated - * @param remappingFunction the function to compute a value - * @return the new value associated with the specified key, or null if none - * @throws NullPointerException if the specified key or remappingFunction - * is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map that would - * otherwise never complete - * @throws RuntimeException or Error if the remappingFunction does so, - * in which case the mapping is unchanged - */ - @SuppressWarnings("unchecked") public V computeIfPresent - (K key, BiFun remappingFunction) { - if (key == null || remappingFunction == null) - throw new NullPointerException(); - return (V)internalCompute(key, true, remappingFunction); - } - - /** - * Computes a new mapping value given a key and - * its current mapped value (or {@code null} if there is no current - * mapping). This is equivalent to - *
 {@code
-     *   value = remappingFunction.apply(key, map.get(key));
-     *   if (value != null)
-     *     map.put(key, value);
-     *   else
-     *     map.remove(key);
-     * }
- * - * except that the action is performed atomically. If the - * function returns {@code null}, the mapping is removed. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and the current mapping is left - * unchanged. Some attempted update operations on this map by - * other threads may be blocked while computation is in progress, - * so the computation should be short and simple, and must not - * attempt to update any other mappings of this Map. For example, - * to either create or append new messages to a value mapping: - * - *
 {@code
-     * Map map = ...;
-     * final String msg = ...;
-     * map.compute(key, new BiFun() {
-     *   public String apply(Key k, String v) {
-     *    return (v == null) ? msg : v + msg;});}}
- * - * @param key key with which the specified value is to be associated - * @param remappingFunction the function to compute a value - * @return the new value associated with the specified key, or null if none - * @throws NullPointerException if the specified key or remappingFunction - * is null - * @throws IllegalStateException if the computation detectably - * attempts a recursive update to this map that would - * otherwise never complete - * @throws RuntimeException or Error if the remappingFunction does so, - * in which case the mapping is unchanged - */ - @SuppressWarnings("unchecked") public V compute - (K key, BiFun remappingFunction) { - if (key == null || remappingFunction == null) - throw new NullPointerException(); - return (V)internalCompute(key, false, remappingFunction); - } - - /** - * If the specified key is not already associated - * with a value, associate it with the given value. - * Otherwise, replace the value with the results of - * the given remapping function. This is equivalent to: - *
 {@code
-     *   if (!map.containsKey(key))
-     *     map.put(value);
-     *   else {
-     *     newValue = remappingFunction.apply(map.get(key), value);
-     *     if (value != null)
-     *       map.put(key, value);
-     *     else
-     *       map.remove(key);
-     *   }
-     * }
- * except that the action is performed atomically. If the - * function returns {@code null}, the mapping is removed. If the - * function itself throws an (unchecked) exception, the exception - * is rethrown to its caller, and the current mapping is left - * unchanged. Some attempted update operations on this map by - * other threads may be blocked while computation is in progress, - * so the computation should be short and simple, and must not - * attempt to update any other mappings of this Map. - */ - @SuppressWarnings("unchecked") public V merge - (K key, V value, BiFun remappingFunction) { - if (key == null || value == null || remappingFunction == null) - throw new NullPointerException(); - return (V)internalMerge(key, value, remappingFunction); - } - - /** - * Removes the key (and its corresponding value) from this map. - * This method does nothing if the key is not in the map. - * - * @param key the key that needs to be removed - * @return the previous value associated with {@code key}, or - * {@code null} if there was no mapping for {@code key} - * @throws NullPointerException if the specified key is null - */ - @SuppressWarnings("unchecked") public V remove(Object key) { - if (key == null) - throw new NullPointerException(); - return (V)internalReplace(key, null, null); - } - - /** - * {@inheritDoc} - * - * @throws NullPointerException if the specified key is null - */ - public boolean remove(Object key, Object value) { - if (key == null) - throw new NullPointerException(); - if (value == null) - return false; - return internalReplace(key, null, value) != null; - } - - /** - * {@inheritDoc} - * - * @throws NullPointerException if any of the arguments are null - */ - public boolean replace(K key, V oldValue, V newValue) { - if (key == null || oldValue == null || newValue == null) - throw new NullPointerException(); - return internalReplace(key, newValue, oldValue) != null; - } - - /** - * {@inheritDoc} - * - * @return the previous value associated with the specified key, - * or {@code null} if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null - */ - @SuppressWarnings("unchecked") public V replace(K key, V value) { - if (key == null || value == null) - throw new NullPointerException(); - return (V)internalReplace(key, value, null); - } - - /** - * Removes all of the mappings from this map. - */ - public void clear() { - internalClear(); - } - - /** - * Returns a {@link Set} view of the keys contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa. - * - * @return the set view - */ - public KeySetView keySet() { - KeySetView ks = keySet; - return (ks != null) ? ks : (keySet = new KeySetView(this, null)); - } - - /** - * Returns a {@link Set} view of the keys in this map, using the - * given common mapped value for any additions (i.e., {@link - * Collection#add} and {@link Collection#addAll}). This is of - * course only appropriate if it is acceptable to use the same - * value for all additions from this view. - * - * @param mappedValue the mapped value to use for any - * additions. - * @return the set view - * @throws NullPointerException if the mappedValue is null - */ - public KeySetView keySet(V mappedValue) { - if (mappedValue == null) - throw new NullPointerException(); - return new KeySetView(this, mappedValue); - } - - /** - * Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are - * reflected in the collection, and vice-versa. - */ - public ValuesView values() { - ValuesView vs = values; - return (vs != null) ? vs : (values = new ValuesView(this)); - } - - /** - * Returns a {@link Set} view of the mappings contained in this map. - * The set is backed by the map, so changes to the map are - * reflected in the set, and vice-versa. The set supports element - * removal, which removes the corresponding mapping from the map, - * via the {@code Iterator.remove}, {@code Set.remove}, - * {@code removeAll}, {@code retainAll}, and {@code clear} - * operations. It does not support the {@code add} or - * {@code addAll} operations. - * - *

The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - */ - public Set> entrySet() { - EntrySetView es = entrySet; - return (es != null) ? es : (entrySet = new EntrySetView(this)); - } - - /** - * Returns an enumeration of the keys in this table. - * - * @return an enumeration of the keys in this table - * @see #keySet() - */ - public Enumeration keys() { - return new KeyIterator(this); - } - - /** - * Returns an enumeration of the values in this table. - * - * @return an enumeration of the values in this table - * @see #values() - */ - public Enumeration elements() { - return new ValueIterator(this); - } - - /** - * Returns a partitionable iterator of the keys in this map. - * - * @return a partitionable iterator of the keys in this map - */ - public Spliterator keySpliterator() { - return new KeyIterator(this); - } - - /** - * Returns a partitionable iterator of the values in this map. - * - * @return a partitionable iterator of the values in this map - */ - public Spliterator valueSpliterator() { - return new ValueIterator(this); - } - - /** - * Returns a partitionable iterator of the entries in this map. - * - * @return a partitionable iterator of the entries in this map - */ - public Spliterator> entrySpliterator() { - return new EntryIterator(this); - } - - /** - * Returns the hash code value for this {@link Map}, i.e., - * the sum of, for each key-value pair in the map, - * {@code key.hashCode() ^ value.hashCode()}. - * - * @return the hash code value for this map - */ - public int hashCode() { - int h = 0; - Traverser it = new Traverser(this); - Object v; - while ((v = it.advance()) != null) { - h += it.nextKey.hashCode() ^ v.hashCode(); - } - return h; - } - - /** - * Returns a string representation of this map. The string - * representation consists of a list of key-value mappings (in no - * particular order) enclosed in braces ("{@code {}}"). Adjacent - * mappings are separated by the characters {@code ", "} (comma - * and space). Each key-value mapping is rendered as the key - * followed by an equals sign ("{@code =}") followed by the - * associated value. - * - * @return a string representation of this map - */ - public String toString() { - Traverser it = new Traverser(this); - StringBuilder sb = new StringBuilder(); - sb.append('{'); - Object v; - if ((v = it.advance()) != null) { - for (;;) { - Object k = it.nextKey; - sb.append(k == this ? "(this Map)" : k); - sb.append('='); - sb.append(v == this ? "(this Map)" : v); - if ((v = it.advance()) == null) - break; - sb.append(',').append(' '); - } - } - return sb.append('}').toString(); - } - - /** - * Compares the specified object with this map for equality. - * Returns {@code true} if the given object is a map with the same - * mappings as this map. This operation may return misleading - * results if either map is concurrently modified during execution - * of this method. - * - * @param o object to be compared for equality with this map - * @return {@code true} if the specified object is equal to this map - */ - public boolean equals(Object o) { - if (o != this) { - if (!(o instanceof Map)) - return false; - Map m = (Map) o; - Traverser it = new Traverser(this); - Object val; - while ((val = it.advance()) != null) { - Object v = m.get(it.nextKey); - if (v == null || (v != val && !v.equals(val))) - return false; - } - for (Map.Entry e : m.entrySet()) { - Object mk, mv, v; - if ((mk = e.getKey()) == null || - (mv = e.getValue()) == null || - (v = internalGet(mk)) == null || - (mv != v && !mv.equals(v))) - return false; - } - } - return true; - } - - /* ----------------Iterators -------------- */ - - @SuppressWarnings("serial") static final class KeyIterator extends Traverser - implements Spliterator, Enumeration { - KeyIterator(ConcurrentHashMapV8 map) { super(map); } - KeyIterator(Traverser it) { - super(it); - } - public KeyIterator split() { - if (nextKey != null) - throw new IllegalStateException(); - return new KeyIterator(this); - } - @SuppressWarnings("unchecked") public final K next() { - if (nextVal == null && advance() == null) - throw new NoSuchElementException(); - Object k = nextKey; - nextVal = null; - return (K) k; - } - - public final K nextElement() { return next(); } - } - - @SuppressWarnings("serial") static final class ValueIterator extends Traverser - implements Spliterator, Enumeration { - ValueIterator(ConcurrentHashMapV8 map) { super(map); } - ValueIterator(Traverser it) { - super(it); - } - public ValueIterator split() { - if (nextKey != null) - throw new IllegalStateException(); - return new ValueIterator(this); - } - - @SuppressWarnings("unchecked") public final V next() { - Object v; - if ((v = nextVal) == null && (v = advance()) == null) - throw new NoSuchElementException(); - nextVal = null; - return (V) v; - } - - public final V nextElement() { return next(); } - } - - @SuppressWarnings("serial") static final class EntryIterator extends Traverser - implements Spliterator> { - EntryIterator(ConcurrentHashMapV8 map) { super(map); } - EntryIterator(Traverser it) { - super(it); - } - public EntryIterator split() { - if (nextKey != null) - throw new IllegalStateException(); - return new EntryIterator(this); - } - - @SuppressWarnings("unchecked") public final Map.Entry next() { - Object v; - if ((v = nextVal) == null && (v = advance()) == null) - throw new NoSuchElementException(); - Object k = nextKey; - nextVal = null; - return new MapEntry((K)k, (V)v, map); - } - } - - /** - * Exported Entry for iterators - */ - static final class MapEntry implements Map.Entry { - final K key; // non-null - V val; // non-null - final ConcurrentHashMapV8 map; - MapEntry(K key, V val, ConcurrentHashMapV8 map) { - this.key = key; - this.val = val; - this.map = map; - } - public final K getKey() { return key; } - public final V getValue() { return val; } - public final int hashCode() { return key.hashCode() ^ val.hashCode(); } - public final String toString(){ return key + "=" + val; } - - public final boolean equals(Object o) { - Object k, v; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry)o).getKey()) != null && - (v = e.getValue()) != null && - (k == key || k.equals(key)) && - (v == val || v.equals(val))); - } - - /** - * Sets our entry's value and writes through to the map. The - * value to return is somewhat arbitrary here. Since we do not - * necessarily track asynchronous changes, the most recent - * "previous" value could be different from what we return (or - * could even have been removed in which case the put will - * re-establish). We do not and cannot guarantee more. - */ - public final V setValue(V value) { - if (value == null) throw new NullPointerException(); - V v = val; - val = value; - map.put(key, value); - return v; - } - } - - /* ---------------- Serialization Support -------------- */ - - /** - * Stripped-down version of helper class used in previous version, - * declared for the sake of serialization compatibility - */ - static class Segment implements Serializable { - private static final long serialVersionUID = 2249069246763182397L; - final float loadFactor; - Segment(float lf) { this.loadFactor = lf; } - } - - /** - * Saves the state of the {@code ConcurrentHashMapV8} instance to a - * stream (i.e., serializes it). - * @param s the stream - * @serialData - * the key (Object) and value (Object) - * for each key-value mapping, followed by a null pair. - * The key-value mappings are emitted in no particular order. - */ - @SuppressWarnings("unchecked") private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - if (segments == null) { // for serialization compatibility - segments = (Segment[]) - new Segment[DEFAULT_CONCURRENCY_LEVEL]; - for (int i = 0; i < segments.length; ++i) - segments[i] = new Segment(LOAD_FACTOR); - } - s.defaultWriteObject(); - Traverser it = new Traverser(this); - Object v; - while ((v = it.advance()) != null) { - s.writeObject(it.nextKey); - s.writeObject(v); - } - s.writeObject(null); - s.writeObject(null); - segments = null; // throw away - } - - /** - * Reconstitutes the instance from a stream (that is, deserializes it). - * @param s the stream - */ - @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - this.segments = null; // unneeded - // initialize transient final field - this.counter = new LongAdder(); - - // Create all nodes, then place in table once size is known - long size = 0L; - Node p = null; - for (;;) { - K k = (K) s.readObject(); - V v = (V) s.readObject(); - if (k != null && v != null) { - int h = spread(k.hashCode()); - p = new Node(h, k, v, p); - ++size; - } - else - break; - } - if (p != null) { - boolean init = false; - int n; - if (size >= (long)(MAXIMUM_CAPACITY >>> 1)) - n = MAXIMUM_CAPACITY; - else { - int sz = (int)size; - n = tableSizeFor(sz + (sz >>> 1) + 1); - } - int sc = sizeCtl; - boolean collide = false; - if (n > sc && - SIZE_CTRL_UPDATER.compareAndSet(this, sc, -1)) { - try { - if (table == null) { - init = true; - AtomicReferenceArray tab = new AtomicReferenceArray(n); - int mask = n - 1; - while (p != null) { - int j = p.hash & mask; - Node next = p.next; - Node q = p.next = tabAt(tab, j); - setTabAt(tab, j, p); - if (!collide && q != null && q.hash == p.hash) - collide = true; - p = next; - } - table = tab; - counter.add(size); - sc = n - (n >>> 2); - } - } finally { - sizeCtl = sc; - } - if (collide) { // rescan and convert to TreeBins - AtomicReferenceArray tab = table; - for (int i = 0; i < tab.length(); ++i) { - int c = 0; - for (Node e = tabAt(tab, i); e != null; e = e.next) { - if (++c > TREE_THRESHOLD && - (e.key instanceof Comparable)) { - replaceWithTreeBin(tab, i, e.key); - break; - } - } - } - } - } - if (!init) { // Can only happen if unsafely published. - while (p != null) { - internalPut(p.key, p.val); - p = p.next; - } - } - } - } - - - // ------------------------------------------------------- - - // Sams - /** Interface describing a void action of one argument */ - public interface Action { void apply(A a); } - /** Interface describing a void action of two arguments */ - public interface BiAction { void apply(A a, B b); } - /** Interface describing a function of one argument */ - public interface Generator { T apply(); } - /** Interface describing a function mapping its argument to a double */ - public interface ObjectToDouble { double apply(A a); } - /** Interface describing a function mapping its argument to a long */ - public interface ObjectToLong { long apply(A a); } - /** Interface describing a function mapping its argument to an int */ - public interface ObjectToInt {int apply(A a); } - /** Interface describing a function mapping two arguments to a double */ - public interface ObjectByObjectToDouble { double apply(A a, B b); } - /** Interface describing a function mapping two arguments to a long */ - public interface ObjectByObjectToLong { long apply(A a, B b); } - /** Interface describing a function mapping two arguments to an int */ - public interface ObjectByObjectToInt {int apply(A a, B b); } - /** Interface describing a function mapping a double to a double */ - public interface DoubleToDouble { double apply(double a); } - /** Interface describing a function mapping a long to a long */ - public interface LongToLong { long apply(long a); } - /** Interface describing a function mapping an int to an int */ - public interface IntToInt { int apply(int a); } - /** Interface describing a function mapping two doubles to a double */ - public interface DoubleByDoubleToDouble { double apply(double a, double b); } - /** Interface describing a function mapping two longs to a long */ - public interface LongByLongToLong { long apply(long a, long b); } - /** Interface describing a function mapping two ints to an int */ - public interface IntByIntToInt { int apply(int a, int b); } - - - /* ----------------Views -------------- */ - - /** - * Base class for views. - */ - static abstract class CHMView { - final ConcurrentHashMapV8 map; - CHMView(ConcurrentHashMapV8 map) { this.map = map; } - - /** - * Returns the map backing this view. - * - * @return the map backing this view - */ - public ConcurrentHashMapV8 getMap() { return map; } - - public final int size() { return map.size(); } - public final boolean isEmpty() { return map.isEmpty(); } - public final void clear() { map.clear(); } - - // implementations below rely on concrete classes supplying these - abstract public Iterator iterator(); - abstract public boolean contains(Object o); - abstract public boolean remove(Object o); - - private static final String oomeMsg = "Required array size too large"; - - public final Object[] toArray() { - long sz = map.mappingCount(); - if (sz > (long)(MAX_ARRAY_SIZE)) - throw new OutOfMemoryError(oomeMsg); - int n = (int)sz; - Object[] r = new Object[n]; - int i = 0; - Iterator it = iterator(); - while (it.hasNext()) { - if (i == n) { - if (n >= MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); - if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) - n = MAX_ARRAY_SIZE; - else - n += (n >>> 1) + 1; - r = Arrays.copyOf(r, n); - } - r[i++] = it.next(); - } - return (i == n) ? r : Arrays.copyOf(r, i); - } - - @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { - long sz = map.mappingCount(); - if (sz > (long)(MAX_ARRAY_SIZE)) - throw new OutOfMemoryError(oomeMsg); - int m = (int)sz; - T[] r = (a.length >= m) ? a : - (T[])java.lang.reflect.Array - .newInstance(a.getClass().getComponentType(), m); - int n = r.length; - int i = 0; - Iterator it = iterator(); - while (it.hasNext()) { - if (i == n) { - if (n >= MAX_ARRAY_SIZE) - throw new OutOfMemoryError(oomeMsg); - if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) - n = MAX_ARRAY_SIZE; - else - n += (n >>> 1) + 1; - r = Arrays.copyOf(r, n); - } - r[i++] = (T)it.next(); - } - if (a == r && i < n) { - r[i] = null; // null-terminate - return r; - } - return (i == n) ? r : Arrays.copyOf(r, i); - } - - public final int hashCode() { - int h = 0; - for (Iterator it = iterator(); it.hasNext();) - h += it.next().hashCode(); - return h; - } - - public final String toString() { - StringBuilder sb = new StringBuilder(); - sb.append('['); - Iterator it = iterator(); - if (it.hasNext()) { - for (;;) { - Object e = it.next(); - sb.append(e == this ? "(this Collection)" : e); - if (!it.hasNext()) - break; - sb.append(',').append(' '); - } - } - return sb.append(']').toString(); - } - - public final boolean containsAll(Collection c) { - if (c != this) { - for (Iterator it = c.iterator(); it.hasNext();) { - Object e = it.next(); - if (e == null || !contains(e)) - return false; - } - } - return true; - } - - public final boolean removeAll(Collection c) { - boolean modified = false; - for (Iterator it = iterator(); it.hasNext();) { - if (c.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - public final boolean retainAll(Collection c) { - boolean modified = false; - for (Iterator it = iterator(); it.hasNext();) { - if (!c.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - } - - /** - * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in - * which additions may optionally be enabled by mapping to a - * common value. This class cannot be directly instantiated. See - * {@link #keySet}, {@link #keySet(Object)}, {@link #newKeySet()}, - * {@link #newKeySet(int)}. - */ - public static class KeySetView extends CHMView implements Set, java.io.Serializable { - private static final long serialVersionUID = 7249069246763182397L; - private final V value; - KeySetView(ConcurrentHashMapV8 map, V value) { // non-public - super(map); - this.value = value; - } - - /** - * Returns the default mapped value for additions, - * or {@code null} if additions are not supported. - * - * @return the default mapped value for additions, or {@code null} - * if not supported. - */ - public V getMappedValue() { return value; } - - // implement Set API - - public boolean contains(Object o) { return map.containsKey(o); } - public boolean remove(Object o) { return map.remove(o) != null; } - - /** - * Returns a "weakly consistent" iterator that will never - * throw {@link ConcurrentModificationException}, and - * guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not - * guaranteed to) reflect any modifications subsequent to - * construction. - * - * @return an iterator over the keys of this map - */ - public Iterator iterator() { return new KeyIterator(map); } - public boolean add(K e) { - V v; - if ((v = value) == null) - throw new UnsupportedOperationException(); - if (e == null) - throw new NullPointerException(); - return map.internalPutIfAbsent(e, v) == null; - } - public boolean addAll(Collection c) { - boolean added = false; - V v; - if ((v = value) == null) - throw new UnsupportedOperationException(); - for (K e : c) { - if (e == null) - throw new NullPointerException(); - if (map.internalPutIfAbsent(e, v) == null) - added = true; - } - return added; - } - public boolean equals(Object o) { - Set c; - return ((o instanceof Set) && - ((c = (Set)o) == this || - (containsAll(c) && c.containsAll(this)))); - } - } - - /** - * A view of a ConcurrentHashMapV8 as a {@link Collection} of - * values, in which additions are disabled. This class cannot be - * directly instantiated. See {@link #values}, - * - *

The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. - */ - public static final class ValuesView extends CHMView - implements Collection { - ValuesView(ConcurrentHashMapV8 map) { super(map); } - public final boolean contains(Object o) { return map.containsValue(o); } - public final boolean remove(Object o) { - if (o != null) { - Iterator it = new ValueIterator(map); - while (it.hasNext()) { - if (o.equals(it.next())) { - it.remove(); - return true; - } - } - } - return false; - } - - /** - * Returns a "weakly consistent" iterator that will never - * throw {@link ConcurrentModificationException}, and - * guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not - * guaranteed to) reflect any modifications subsequent to - * construction. - * - * @return an iterator over the values of this map - */ - public final Iterator iterator() { - return new ValueIterator(map); - } - public final boolean add(V e) { - throw new UnsupportedOperationException(); - } - public final boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - } - - /** - * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) - * entries. This class cannot be directly instantiated. See - * {@link #entrySet}. - */ - public static final class EntrySetView extends CHMView - implements Set> { - EntrySetView(ConcurrentHashMapV8 map) { super(map); } - public final boolean contains(Object o) { - Object k, v, r; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry)o).getKey()) != null && - (r = map.get(k)) != null && - (v = e.getValue()) != null && - (v == r || v.equals(r))); - } - public final boolean remove(Object o) { - Object k, v; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry)o).getKey()) != null && - (v = e.getValue()) != null && - map.remove(k, v)); - } - - /** - * Returns a "weakly consistent" iterator that will never - * throw {@link ConcurrentModificationException}, and - * guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not - * guaranteed to) reflect any modifications subsequent to - * construction. - * - * @return an iterator over the entries of this map - */ - public final Iterator> iterator() { - return new EntryIterator(map); - } - - public final boolean add(Entry e) { - K key = e.getKey(); - V value = e.getValue(); - if (key == null || value == null) - throw new NullPointerException(); - return map.internalPut(key, value) == null; - } - public final boolean addAll(Collection> c) { - boolean added = false; - for (Entry e : c) { - if (add(e)) - added = true; - } - return added; - } - public boolean equals(Object o) { - Set c; - return ((o instanceof Set) && - ((c = (Set)o) == this || - (containsAll(c) && c.containsAll(this)))); - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,204 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on 1.9 version. - -package com.concurrent_ruby.ext.jsr166e.nounsafe; - -import java.util.concurrent.atomic.AtomicLong; -import java.io.IOException; -import java.io.Serializable; -import java.io.ObjectInputStream; - -/** - * One or more variables that together maintain an initially zero - * {@code long} sum. When updates (method {@link #add}) are contended - * across threads, the set of variables may grow dynamically to reduce - * contention. Method {@link #sum} (or, equivalently, {@link - * #longValue}) returns the current total combined across the - * variables maintaining the sum. - * - *

This class is usually preferable to {@link AtomicLong} when - * multiple threads update a common sum that is used for purposes such - * as collecting statistics, not for fine-grained synchronization - * control. Under low update contention, the two classes have similar - * characteristics. But under high contention, expected throughput of - * this class is significantly higher, at the expense of higher space - * consumption. - * - *

This class extends {@link Number}, but does not define - * methods such as {@code hashCode} and {@code compareTo} because - * instances are expected to be mutated, and so are not useful as - * collection keys. - * - *

jsr166e note: This class is targeted to be placed in - * java.util.concurrent.atomic. - * - * @since 1.8 - * @author Doug Lea - */ -public class LongAdder extends Striped64 implements Serializable { - private static final long serialVersionUID = 7249069246863182397L; - - /** - * Version of plus for use in retryUpdate - */ - final long fn(long v, long x) { return v + x; } - - /** - * Creates a new adder with initial sum of zero. - */ - public LongAdder() { - } - - /** - * Adds the given value. - * - * @param x the value to add - */ - public void add(long x) { - Cell[] as; long b, v; HashCode hc; Cell a; int n; - if ((as = cells) != null || !casBase(b = base, b + x)) { - boolean uncontended = true; - int h = (hc = threadHashCode.get()).code; - if (as == null || (n = as.length) < 1 || - (a = as[(n - 1) & h]) == null || - !(uncontended = a.cas(v = a.value, v + x))) - retryUpdate(x, hc, uncontended); - } - } - - /** - * Equivalent to {@code add(1)}. - */ - public void increment() { - add(1L); - } - - /** - * Equivalent to {@code add(-1)}. - */ - public void decrement() { - add(-1L); - } - - /** - * Returns the current sum. The returned value is NOT an - * atomic snapshot: Invocation in the absence of concurrent - * updates returns an accurate result, but concurrent updates that - * occur while the sum is being calculated might not be - * incorporated. - * - * @return the sum - */ - public long sum() { - long sum = base; - Cell[] as = cells; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) - sum += a.value; - } - } - return sum; - } - - /** - * Resets variables maintaining the sum to zero. This method may - * be a useful alternative to creating a new adder, but is only - * effective if there are no concurrent updates. Because this - * method is intrinsically racy, it should only be used when it is - * known that no threads are concurrently updating. - */ - public void reset() { - internalReset(0L); - } - - /** - * Equivalent in effect to {@link #sum} followed by {@link - * #reset}. This method may apply for example during quiescent - * points between multithreaded computations. If there are - * updates concurrent with this method, the returned value is - * not guaranteed to be the final value occurring before - * the reset. - * - * @return the sum - */ - public long sumThenReset() { - long sum = base; - Cell[] as = cells; - base = 0L; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) { - sum += a.value; - a.value = 0L; - } - } - } - return sum; - } - - /** - * Returns the String representation of the {@link #sum}. - * @return the String representation of the {@link #sum} - */ - public String toString() { - return Long.toString(sum()); - } - - /** - * Equivalent to {@link #sum}. - * - * @return the sum - */ - public long longValue() { - return sum(); - } - - /** - * Returns the {@link #sum} as an {@code int} after a narrowing - * primitive conversion. - */ - public int intValue() { - return (int)sum(); - } - - /** - * Returns the {@link #sum} as a {@code float} - * after a widening primitive conversion. - */ - public float floatValue() { - return (float)sum(); - } - - /** - * Returns the {@link #sum} as a {@code double} after a widening - * primitive conversion. - */ - public double doubleValue() { - return (double)sum(); - } - - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeLong(sum()); - } - - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - s.defaultReadObject(); - busy = 0; - cells = null; - base = s.readLong(); - } - -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,291 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on 1.5 version. - -package com.concurrent_ruby.ext.jsr166e.nounsafe; - -import java.util.Random; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.concurrent.atomic.AtomicLongFieldUpdater; - -/** - * A package-local class holding common representation and mechanics - * for classes supporting dynamic striping on 64bit values. The class - * extends Number so that concrete subclasses must publicly do so. - */ -abstract class Striped64 extends Number { - /* - * This class maintains a lazily-initialized table of atomically - * updated variables, plus an extra "base" field. The table size - * is a power of two. Indexing uses masked per-thread hash codes. - * Nearly all declarations in this class are package-private, - * accessed directly by subclasses. - * - * Table entries are of class Cell; a variant of AtomicLong padded - * to reduce cache contention on most processors. Padding is - * overkill for most Atomics because they are usually irregularly - * scattered in memory and thus don't interfere much with each - * other. But Atomic objects residing in arrays will tend to be - * placed adjacent to each other, and so will most often share - * cache lines (with a huge negative performance impact) without - * this precaution. - * - * In part because Cells are relatively large, we avoid creating - * them until they are needed. When there is no contention, all - * updates are made to the base field. Upon first contention (a - * failed CAS on base update), the table is initialized to size 2. - * The table size is doubled upon further contention until - * reaching the nearest power of two greater than or equal to the - * number of CPUS. Table slots remain empty (null) until they are - * needed. - * - * A single spinlock ("busy") is used for initializing and - * resizing the table, as well as populating slots with new Cells. - * There is no need for a blocking lock: When the lock is not - * available, threads try other slots (or the base). During these - * retries, there is increased contention and reduced locality, - * which is still better than alternatives. - * - * Per-thread hash codes are initialized to random values. - * Contention and/or table collisions are indicated by failed - * CASes when performing an update operation (see method - * retryUpdate). Upon a collision, if the table size is less than - * the capacity, it is doubled in size unless some other thread - * holds the lock. If a hashed slot is empty, and lock is - * available, a new Cell is created. Otherwise, if the slot - * exists, a CAS is tried. Retries proceed by "double hashing", - * using a secondary hash (Marsaglia XorShift) to try to find a - * free slot. - * - * The table size is capped because, when there are more threads - * than CPUs, supposing that each thread were bound to a CPU, - * there would exist a perfect hash function mapping threads to - * slots that eliminates collisions. When we reach capacity, we - * search for this mapping by randomly varying the hash codes of - * colliding threads. Because search is random, and collisions - * only become known via CAS failures, convergence can be slow, - * and because threads are typically not bound to CPUS forever, - * may not occur at all. However, despite these limitations, - * observed contention rates are typically low in these cases. - * - * It is possible for a Cell to become unused when threads that - * once hashed to it terminate, as well as in the case where - * doubling the table causes no thread to hash to it under - * expanded mask. We do not try to detect or remove such cells, - * under the assumption that for long-running instances, observed - * contention levels will recur, so the cells will eventually be - * needed again; and for short-lived ones, it does not matter. - */ - - /** - * Padded variant of AtomicLong supporting only raw accesses plus CAS. - * The value field is placed between pads, hoping that the JVM doesn't - * reorder them. - * - * JVM intrinsics note: It would be possible to use a release-only - * form of CAS here, if it were provided. - */ - static final class Cell { - volatile long p0, p1, p2, p3, p4, p5, p6; - volatile long value; - volatile long q0, q1, q2, q3, q4, q5, q6; - - static AtomicLongFieldUpdater VALUE_UPDATER = AtomicLongFieldUpdater.newUpdater(Cell.class, "value"); - - Cell(long x) { value = x; } - - final boolean cas(long cmp, long val) { - return VALUE_UPDATER.compareAndSet(this, cmp, val); - } - - } - - /** - * Holder for the thread-local hash code. The code is initially - * random, but may be set to a different value upon collisions. - */ - static final class HashCode { - static final Random rng = new Random(); - int code; - HashCode() { - int h = rng.nextInt(); // Avoid zero to allow xorShift rehash - code = (h == 0) ? 1 : h; - } - } - - /** - * The corresponding ThreadLocal class - */ - static final class ThreadHashCode extends ThreadLocal { - public HashCode initialValue() { return new HashCode(); } - } - - /** - * Static per-thread hash codes. Shared across all instances to - * reduce ThreadLocal pollution and because adjustments due to - * collisions in one table are likely to be appropriate for - * others. - */ - static final ThreadHashCode threadHashCode = new ThreadHashCode(); - - /** Number of CPUS, to place bound on table size */ - static final int NCPU = Runtime.getRuntime().availableProcessors(); - - /** - * Table of cells. When non-null, size is a power of 2. - */ - transient volatile Cell[] cells; - - /** - * Base value, used mainly when there is no contention, but also as - * a fallback during table initialization races. Updated via CAS. - */ - transient volatile long base; - - /** - * Spinlock (locked via CAS) used when resizing and/or creating Cells. - */ - transient volatile int busy; - - AtomicLongFieldUpdater BASE_UPDATER = AtomicLongFieldUpdater.newUpdater(Striped64.class, "base"); - AtomicIntegerFieldUpdater BUSY_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Striped64.class, "busy"); - - /** - * Package-private default constructor - */ - Striped64() { - } - - /** - * CASes the base field. - */ - final boolean casBase(long cmp, long val) { - return BASE_UPDATER.compareAndSet(this, cmp, val); - } - - /** - * CASes the busy field from 0 to 1 to acquire lock. - */ - final boolean casBusy() { - return BUSY_UPDATER.compareAndSet(this, 0, 1); - } - - /** - * Computes the function of current and new value. Subclasses - * should open-code this update function for most uses, but the - * virtualized form is needed within retryUpdate. - * - * @param currentValue the current value (of either base or a cell) - * @param newValue the argument from a user update call - * @return result of the update function - */ - abstract long fn(long currentValue, long newValue); - - /** - * Handles cases of updates involving initialization, resizing, - * creating new Cells, and/or contention. See above for - * explanation. This method suffers the usual non-modularity - * problems of optimistic retry code, relying on rechecked sets of - * reads. - * - * @param x the value - * @param hc the hash code holder - * @param wasUncontended false if CAS failed before call - */ - final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { - int h = hc.code; - boolean collide = false; // True if last slot nonempty - for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { - if (busy == 0) { // Try to attach new Cell - Cell r = new Cell(x); // Optimistically create - if (busy == 0 && casBusy()) { - boolean created = false; - try { // Recheck under lock - Cell[] rs; int m, j; - if ((rs = cells) != null && - (m = rs.length) > 0 && - rs[j = (m - 1) & h] == null) { - rs[j] = r; - created = true; - } - } finally { - busy = 0; - } - if (created) - break; - continue; // Slot is now non-empty - } - } - collide = false; - } - else if (!wasUncontended) // CAS already known to fail - wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, fn(v, x))) - break; - else if (n >= NCPU || cells != as) - collide = false; // At max size or stale - else if (!collide) - collide = true; - else if (busy == 0 && casBusy()) { - try { - if (cells == as) { // Expand table unless stale - Cell[] rs = new Cell[n << 1]; - for (int i = 0; i < n; ++i) - rs[i] = as[i]; - cells = rs; - } - } finally { - busy = 0; - } - collide = false; - continue; // Retry with expanded table - } - h ^= h << 13; // Rehash - h ^= h >>> 17; - h ^= h << 5; - } - else if (busy == 0 && cells == as && casBusy()) { - boolean init = false; - try { // Initialize table - if (cells == as) { - Cell[] rs = new Cell[2]; - rs[h & 1] = new Cell(x); - cells = rs; - init = true; - } - } finally { - busy = 0; - } - if (init) - break; - } - else if (casBase(v = base, fn(v, x))) - break; // Fall back on using base - } - hc.code = h; // Record index for next time - } - - - /** - * Sets base and all cells to the given value. - */ - final void internalReset(long initialValue) { - Cell[] as = cells; - base = initialValue; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) - a.value = initialValue; - } - } - } -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,199 +0,0 @@ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// This is based on 1.16 version - -package com.concurrent_ruby.ext.jsr166y; - -import java.util.Random; - -/** - * A random number generator isolated to the current thread. Like the - * global {@link java.util.Random} generator used by the {@link - * java.lang.Math} class, a {@code ThreadLocalRandom} is initialized - * with an internally generated seed that may not otherwise be - * modified. When applicable, use of {@code ThreadLocalRandom} rather - * than shared {@code Random} objects in concurrent programs will - * typically encounter much less overhead and contention. Use of - * {@code ThreadLocalRandom} is particularly appropriate when multiple - * tasks (for example, each a {@link ForkJoinTask}) use random numbers - * in parallel in thread pools. - * - *

Usages of this class should typically be of the form: - * {@code ThreadLocalRandom.current().nextX(...)} (where - * {@code X} is {@code Int}, {@code Long}, etc). - * When all usages are of this form, it is never possible to - * accidently share a {@code ThreadLocalRandom} across multiple threads. - * - *

This class also provides additional commonly used bounded random - * generation methods. - * - * @since 1.7 - * @author Doug Lea - */ -public class ThreadLocalRandom extends Random { - // same constants as Random, but must be redeclared because private - private static final long multiplier = 0x5DEECE66DL; - private static final long addend = 0xBL; - private static final long mask = (1L << 48) - 1; - - /** - * The random seed. We can't use super.seed. - */ - private long rnd; - - /** - * Initialization flag to permit calls to setSeed to succeed only - * while executing the Random constructor. We can't allow others - * since it would cause setting seed in one part of a program to - * unintentionally impact other usages by the thread. - */ - boolean initialized; - - // Padding to help avoid memory contention among seed updates in - // different TLRs in the common case that they are located near - // each other. - private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; - - /** - * The actual ThreadLocal - */ - private static final ThreadLocal localRandom = - new ThreadLocal() { - protected ThreadLocalRandom initialValue() { - return new ThreadLocalRandom(); - } - }; - - - /** - * Constructor called only by localRandom.initialValue. - */ - ThreadLocalRandom() { - super(); - initialized = true; - } - - /** - * Returns the current thread's {@code ThreadLocalRandom}. - * - * @return the current thread's {@code ThreadLocalRandom} - */ - public static ThreadLocalRandom current() { - return localRandom.get(); - } - - /** - * Throws {@code UnsupportedOperationException}. Setting seeds in - * this generator is not supported. - * - * @throws UnsupportedOperationException always - */ - public void setSeed(long seed) { - if (initialized) - throw new UnsupportedOperationException(); - rnd = (seed ^ multiplier) & mask; - } - - protected int next(int bits) { - rnd = (rnd * multiplier + addend) & mask; - return (int) (rnd >>> (48-bits)); - } - - /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). - * - * @param least the least value returned - * @param bound the upper bound (exclusive) - * @throws IllegalArgumentException if least greater than or equal - * to bound - * @return the next value - */ - public int nextInt(int least, int bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextInt(bound - least) + least; - } - - /** - * Returns a pseudorandom, uniformly distributed value - * between 0 (inclusive) and the specified value (exclusive). - * - * @param n the bound on the random number to be returned. Must be - * positive. - * @return the next value - * @throws IllegalArgumentException if n is not positive - */ - public long nextLong(long n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - // Divide n by two until small enough for nextInt. On each - // iteration (at most 31 of them but usually much less), - // randomly choose both whether to include high bit in result - // (offset) and whether to continue with the lower vs upper - // half (which makes a difference only if odd). - long offset = 0; - while (n >= Integer.MAX_VALUE) { - int bits = next(2); - long half = n >>> 1; - long nextn = ((bits & 2) == 0) ? half : n - half; - if ((bits & 1) == 0) - offset += n - nextn; - n = nextn; - } - return offset + nextInt((int) n); - } - - /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). - * - * @param least the least value returned - * @param bound the upper bound (exclusive) - * @return the next value - * @throws IllegalArgumentException if least greater than or equal - * to bound - */ - public long nextLong(long least, long bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextLong(bound - least) + least; - } - - /** - * Returns a pseudorandom, uniformly distributed {@code double} value - * between 0 (inclusive) and the specified value (exclusive). - * - * @param n the bound on the random number to be returned. Must be - * positive. - * @return the next value - * @throws IllegalArgumentException if n is not positive - */ - public double nextDouble(double n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - return nextDouble() * n; - } - - /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). - * - * @param least the least value returned - * @param bound the upper bound (exclusive) - * @return the next value - * @throws IllegalArgumentException if least greater than or equal - * to bound - */ - public double nextDouble(double least, double bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextDouble() * (bound - least) + least; - } - - private static final long serialVersionUID = -5851777807851030925L; -} diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/agent.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,587 +0,0 @@ -require 'concurrent/configuration' -require 'concurrent/atomic/atomic_reference' -require 'concurrent/atomic/thread_local_var' -require 'concurrent/collection/copy_on_write_observer_set' -require 'concurrent/concern/observable' -require 'concurrent/synchronization' - -module Concurrent - - # `Agent` is inspired by Clojure's [agent](http://clojure.org/agents) - # function. An agent is a shared, mutable variable providing independent, - # uncoordinated, *asynchronous* change of individual values. Best used when - # the value will undergo frequent, complex updates. Suitable when the result - # of an update does not need to be known immediately. `Agent` is (mostly) - # functionally equivalent to Clojure's agent, except where the runtime - # prevents parity. - # - # Agents are reactive, not autonomous - there is no imperative message loop - # and no blocking receive. The state of an Agent should be itself immutable - # and the `#value` of an Agent is always immediately available for reading by - # any thread without any messages, i.e. observation does not require - # cooperation or coordination. - # - # Agent action dispatches are made using the various `#send` methods. These - # methods always return immediately. At some point later, in another thread, - # the following will happen: - # - # 1. The given `action` will be applied to the state of the Agent and the - # `args`, if any were supplied. - # 2. The return value of `action` will be passed to the validator lambda, - # if one has been set on the Agent. - # 3. If the validator succeeds or if no validator was given, the return value - # of the given `action` will become the new `#value` of the Agent. See - # `#initialize` for details. - # 4. If any observers were added to the Agent, they will be notified. See - # `#add_observer` for details. - # 5. If during the `action` execution any other dispatches are made (directly - # or indirectly), they will be held until after the `#value` of the Agent - # has been changed. - # - # If any exceptions are thrown by an action function, no nested dispatches - # will occur, and the exception will be cached in the Agent itself. When an - # Agent has errors cached, any subsequent interactions will immediately throw - # an exception, until the agent's errors are cleared. Agent errors can be - # examined with `#error` and the agent restarted with `#restart`. - # - # The actions of all Agents get interleaved amongst threads in a thread pool. - # At any point in time, at most one action for each Agent is being executed. - # Actions dispatched to an agent from another single agent or thread will - # occur in the order they were sent, potentially interleaved with actions - # dispatched to the same agent from other sources. The `#send` method should - # be used for actions that are CPU limited, while the `#send_off` method is - # appropriate for actions that may block on IO. - # - # Unlike in Clojure, `Agent` cannot participate in `Concurrent::TVar` transactions. - # - # ## Example - # - # ``` - # def next_fibonacci(set = nil) - # return [0, 1] if set.nil? - # set + [set[-2..-1].reduce{|sum,x| sum + x }] - # end - # - # # create an agent with an initial value - # agent = Concurrent::Agent.new(next_fibonacci) - # - # # send a few update requests - # 5.times do - # agent.send{|set| next_fibonacci(set) } - # end - # - # # wait for them to complete - # agent.await - # - # # get the current value - # agent.value #=> [0, 1, 1, 2, 3, 5, 8] - # ``` - # - # ## Observation - # - # Agents support observers through the {Concurrent::Observable} mixin module. - # Notification of observers occurs every time an action dispatch returns and - # the new value is successfully validated. Observation will *not* occur if the - # action raises an exception, if validation fails, or when a {#restart} occurs. - # - # When notified the observer will receive three arguments: `time`, `old_value`, - # and `new_value`. The `time` argument is the time at which the value change - # occurred. The `old_value` is the value of the Agent when the action began - # processing. The `new_value` is the value to which the Agent was set when the - # action completed. Note that `old_value` and `new_value` may be the same. - # This is not an error. It simply means that the action returned the same - # value. - # - # ## Nested Actions - # - # It is possible for an Agent action to post further actions back to itself. - # The nested actions will be enqueued normally then processed *after* the - # outer action completes, in the order they were sent, possibly interleaved - # with action dispatches from other threads. Nested actions never deadlock - # with one another and a failure in a nested action will never affect the - # outer action. - # - # Nested actions can be called using the Agent reference from the enclosing - # scope or by passing the reference in as a "send" argument. Nested actions - # cannot be post using `self` from within the action block/proc/lambda; `self` - # in this context will not reference the Agent. The preferred method for - # dispatching nested actions is to pass the Agent as an argument. This allows - # Ruby to more effectively manage the closing scope. - # - # Prefer this: - # - # ``` - # agent = Concurrent::Agent.new(0) - # agent.send(agent) do |value, this| - # this.send {|v| v + 42 } - # 3.14 - # end - # agent.value #=> 45.14 - # ``` - # - # Over this: - # - # ``` - # agent = Concurrent::Agent.new(0) - # agent.send do |value| - # agent.send {|v| v + 42 } - # 3.14 - # end - # ``` - # - # @!macro agent_await_warning - # - # **NOTE** Never, *under any circumstances*, call any of the "await" methods - # ({#await}, {#await_for}, {#await_for!}, and {#wait}) from within an action - # block/proc/lambda. The call will block the Agent and will always fail. - # Calling either {#await} or {#wait} (with a timeout of `nil`) will - # hopelessly deadlock the Agent with no possibility of recovery. - # - # @!macro thread_safe_variable_comparison - # - # @see http://clojure.org/Agents Clojure Agents - # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State - class Agent < Synchronization::LockableObject - include Concern::Observable - - ERROR_MODES = [:continue, :fail].freeze - private_constant :ERROR_MODES - - AWAIT_FLAG = ::Object.new - private_constant :AWAIT_FLAG - - AWAIT_ACTION = ->(value, latch) { latch.count_down; AWAIT_FLAG } - private_constant :AWAIT_ACTION - - DEFAULT_ERROR_HANDLER = ->(agent, error) { nil } - private_constant :DEFAULT_ERROR_HANDLER - - DEFAULT_VALIDATOR = ->(value) { true } - private_constant :DEFAULT_VALIDATOR - - Job = Struct.new(:action, :args, :executor, :caller) - private_constant :Job - - # Raised during action processing or any other time in an Agent's lifecycle. - class Error < StandardError - def initialize(message = nil) - message ||= 'agent must be restarted before jobs can post' - super(message) - end - end - - # Raised when a new value obtained during action processing or at `#restart` - # fails validation. - class ValidationError < Error - def initialize(message = nil) - message ||= 'invalid value' - super(message) - end - end - - # The error mode this Agent is operating in. See {#initialize} for details. - attr_reader :error_mode - - # Create a new `Agent` with the given initial value and options. - # - # The `:validator` option must be `nil` or a side-effect free proc/lambda - # which takes one argument. On any intended value change the validator, if - # provided, will be called. If the new value is invalid the validator should - # return `false` or raise an error. - # - # The `:error_handler` option must be `nil` or a proc/lambda which takes two - # arguments. When an action raises an error or validation fails, either by - # returning false or raising an error, the error handler will be called. The - # arguments to the error handler will be a reference to the agent itself and - # the error object which was raised. - # - # The `:error_mode` may be either `:continue` (the default if an error - # handler is given) or `:fail` (the default if error handler nil or not - # given). - # - # If an action being run by the agent throws an error or doesn't pass - # validation the error handler, if present, will be called. After the - # handler executes if the error mode is `:continue` the Agent will continue - # as if neither the action that caused the error nor the error itself ever - # happened. - # - # If the mode is `:fail` the Agent will become {#failed?} and will stop - # accepting new action dispatches. Any previously queued actions will be - # held until {#restart} is called. The {#value} method will still work, - # returning the value of the Agent before the error. - # - # @param [Object] initial the initial value - # @param [Hash] opts the configuration options - # - # @option opts [Symbol] :error_mode either `:continue` or `:fail` - # @option opts [nil, Proc] :error_handler the (optional) error handler - # @option opts [nil, Proc] :validator the (optional) validation procedure - def initialize(initial, opts = {}) - super() - synchronize { ns_initialize(initial, opts) } - end - - # The current value (state) of the Agent, irrespective of any pending or - # in-progress actions. The value is always available and is non-blocking. - # - # @return [Object] the current value - def value - @current.value # TODO (pitr 12-Sep-2015): broken unsafe read? - end - - alias_method :deref, :value - - # When {#failed?} and {#error_mode} is `:fail`, returns the error object - # which caused the failure, else `nil`. When {#error_mode} is `:continue` - # will *always* return `nil`. - # - # @return [nil, Error] the error which caused the failure when {#failed?} - def error - @error.value - end - - alias_method :reason, :error - - # @!macro agent_send - # - # Dispatches an action to the Agent and returns immediately. Subsequently, - # in a thread from a thread pool, the {#value} will be set to the return - # value of the action. Action dispatches are only allowed when the Agent - # is not {#failed?}. - # - # The action must be a block/proc/lambda which takes 1 or more arguments. - # The first argument is the current {#value} of the Agent. Any arguments - # passed to the send method via the `args` parameter will be passed to the - # action as the remaining arguments. The action must return the new value - # of the Agent. - # - # * {#send} and {#send!} should be used for actions that are CPU limited - # * {#send_off}, {#send_off!}, and {#<<} are appropriate for actions that - # may block on IO - # * {#send_via} and {#send_via!} are used when a specific executor is to - # be used for the action - # - # @param [Array] args zero or more arguments to be passed to - # the action - # @param [Proc] action the action dispatch to be enqueued - # - # @yield [agent, value, *args] process the old value and return the new - # @yieldparam [Object] value the current {#value} of the Agent - # @yieldparam [Array] args zero or more arguments to pass to the - # action - # @yieldreturn [Object] the new value of the Agent - # - # @!macro send_return - # @return [Boolean] true if the action is successfully enqueued, false if - # the Agent is {#failed?} - def send(*args, &action) - enqueue_action_job(action, args, Concurrent.global_fast_executor) - end - - # @!macro agent_send - # - # @!macro send_bang_return_and_raise - # @return [Boolean] true if the action is successfully enqueued - # @raise [Concurrent::Agent::Error] if the Agent is {#failed?} - def send!(*args, &action) - raise Error.new unless send(*args, &action) - true - end - - # @!macro agent_send - # @!macro send_return - def send_off(*args, &action) - enqueue_action_job(action, args, Concurrent.global_io_executor) - end - - alias_method :post, :send_off - - # @!macro agent_send - # @!macro send_bang_return_and_raise - def send_off!(*args, &action) - raise Error.new unless send_off(*args, &action) - true - end - - # @!macro agent_send - # @!macro send_return - # @param [Concurrent::ExecutorService] executor the executor on which the - # action is to be dispatched - def send_via(executor, *args, &action) - enqueue_action_job(action, args, executor) - end - - # @!macro agent_send - # @!macro send_bang_return_and_raise - # @param [Concurrent::ExecutorService] executor the executor on which the - # action is to be dispatched - def send_via!(executor, *args, &action) - raise Error.new unless send_via(executor, *args, &action) - true - end - - # Dispatches an action to the Agent and returns immediately. Subsequently, - # in a thread from a thread pool, the {#value} will be set to the return - # value of the action. Appropriate for actions that may block on IO. - # - # @param [Proc] action the action dispatch to be enqueued - # @return [Concurrent::Agent] self - # @see #send_off - def <<(action) - send_off(&action) - self - end - - # Blocks the current thread (indefinitely!) until all actions dispatched - # thus far, from this thread or nested by the Agent, have occurred. Will - # block when {#failed?}. Will never return if a failed Agent is {#restart} - # with `:clear_actions` true. - # - # Returns a reference to `self` to support method chaining: - # - # ``` - # current_value = agent.await.value - # ``` - # - # @return [Boolean] self - # - # @!macro agent_await_warning - def await - wait(nil) - self - end - - # Blocks the current thread until all actions dispatched thus far, from this - # thread or nested by the Agent, have occurred, or the timeout (in seconds) - # has elapsed. - # - # @param [Float] timeout the maximum number of seconds to wait - # @return [Boolean] true if all actions complete before timeout else false - # - # @!macro agent_await_warning - def await_for(timeout) - wait(timeout.to_f) - end - - # Blocks the current thread until all actions dispatched thus far, from this - # thread or nested by the Agent, have occurred, or the timeout (in seconds) - # has elapsed. - # - # @param [Float] timeout the maximum number of seconds to wait - # @return [Boolean] true if all actions complete before timeout - # - # @raise [Concurrent::TimeoutError] when timout is reached - # - # @!macro agent_await_warning - def await_for!(timeout) - raise Concurrent::TimeoutError unless wait(timeout.to_f) - true - end - - # Blocks the current thread until all actions dispatched thus far, from this - # thread or nested by the Agent, have occurred, or the timeout (in seconds) - # has elapsed. Will block indefinitely when timeout is nil or not given. - # - # Provided mainly for consistency with other classes in this library. Prefer - # the various `await` methods instead. - # - # @param [Float] timeout the maximum number of seconds to wait - # @return [Boolean] true if all actions complete before timeout else false - # - # @!macro agent_await_warning - def wait(timeout = nil) - latch = Concurrent::CountDownLatch.new(1) - enqueue_await_job(latch) - latch.wait(timeout) - end - - # Is the Agent in a failed state? - # - # @see #restart - def failed? - !@error.value.nil? - end - - alias_method :stopped?, :failed? - - # When an Agent is {#failed?}, changes the Agent {#value} to `new_value` - # then un-fails the Agent so that action dispatches are allowed again. If - # the `:clear_actions` option is give and true, any actions queued on the - # Agent that were being held while it was failed will be discarded, - # otherwise those held actions will proceed. The `new_value` must pass the - # validator if any, or `restart` will raise an exception and the Agent will - # remain failed with its old {#value} and {#error}. Observers, if any, will - # not be notified of the new state. - # - # @param [Object] new_value the new value for the Agent once restarted - # @param [Hash] opts the configuration options - # @option opts [Symbol] :clear_actions true if all enqueued but unprocessed - # actions should be discarded on restart, else false (default: false) - # @return [Boolean] true - # - # @raise [Concurrent:AgentError] when not failed - def restart(new_value, opts = {}) - clear_actions = opts.fetch(:clear_actions, false) - synchronize do - raise Error.new('agent is not failed') unless failed? - raise ValidationError unless ns_validate(new_value) - @current.value = new_value - @error.value = nil - @queue.clear if clear_actions - ns_post_next_job unless @queue.empty? - end - true - end - - class << self - - # Blocks the current thread (indefinitely!) until all actions dispatched - # thus far to all the given Agents, from this thread or nested by the - # given Agents, have occurred. Will block when any of the agents are - # failed. Will never return if a failed Agent is restart with - # `:clear_actions` true. - # - # @param [Array] agents the Agents on which to wait - # @return [Boolean] true - # - # @!macro agent_await_warning - def await(*agents) - agents.each { |agent| agent.await } - true - end - - # Blocks the current thread until all actions dispatched thus far to all - # the given Agents, from this thread or nested by the given Agents, have - # occurred, or the timeout (in seconds) has elapsed. - # - # @param [Float] timeout the maximum number of seconds to wait - # @param [Array] agents the Agents on which to wait - # @return [Boolean] true if all actions complete before timeout else false - # - # @!macro agent_await_warning - def await_for(timeout, *agents) - end_at = Concurrent.monotonic_time + timeout.to_f - ok = agents.length.times do |i| - break false if (delay = end_at - Concurrent.monotonic_time) < 0 - break false unless agents[i].await_for(delay) - end - !!ok - end - - # Blocks the current thread until all actions dispatched thus far to all - # the given Agents, from this thread or nested by the given Agents, have - # occurred, or the timeout (in seconds) has elapsed. - # - # @param [Float] timeout the maximum number of seconds to wait - # @param [Array] agents the Agents on which to wait - # @return [Boolean] true if all actions complete before timeout - # - # @raise [Concurrent::TimeoutError] when timout is reached - # @!macro agent_await_warning - def await_for!(timeout, *agents) - raise Concurrent::TimeoutError unless await_for(timeout, *agents) - true - end - end - - private - - def ns_initialize(initial, opts) - @error_mode = opts[:error_mode] - @error_handler = opts[:error_handler] - - if @error_mode && !ERROR_MODES.include?(@error_mode) - raise ArgumentError.new('unrecognized error mode') - elsif @error_mode.nil? - @error_mode = @error_handler ? :continue : :fail - end - - @error_handler ||= DEFAULT_ERROR_HANDLER - @validator = opts.fetch(:validator, DEFAULT_VALIDATOR) - @current = Concurrent::AtomicReference.new(initial) - @error = Concurrent::AtomicReference.new(nil) - @caller = Concurrent::ThreadLocalVar.new(nil) - @queue = [] - - self.observers = Collection::CopyOnNotifyObserverSet.new - end - - def enqueue_action_job(action, args, executor) - raise ArgumentError.new('no action given') unless action - job = Job.new(action, args, executor, @caller.value || Thread.current.object_id) - synchronize { ns_enqueue_job(job) } - end - - def enqueue_await_job(latch) - synchronize do - if (index = ns_find_last_job_for_thread) - job = Job.new(AWAIT_ACTION, [latch], Concurrent.global_immediate_executor, - Thread.current.object_id) - ns_enqueue_job(job, index+1) - else - latch.count_down - true - end - end - end - - def ns_enqueue_job(job, index = nil) - # a non-nil index means this is an await job - return false if index.nil? && failed? - index ||= @queue.length - @queue.insert(index, job) - # if this is the only job, post to executor - ns_post_next_job if @queue.length == 1 - true - end - - def ns_post_next_job - @queue.first.executor.post { execute_next_job } - end - - def execute_next_job - job = synchronize { @queue.first } - old_value = @current.value - - @caller.value = job.caller # for nested actions - new_value = job.action.call(old_value, *job.args) - @caller.value = nil - - return if new_value == AWAIT_FLAG - - if ns_validate(new_value) - @current.value = new_value - observers.notify_observers(Time.now, old_value, new_value) - else - handle_error(ValidationError.new) - end - rescue => error - handle_error(error) - ensure - synchronize do - @queue.shift - unless failed? || @queue.empty? - ns_post_next_job - end - end - end - - def ns_validate(value) - @validator.call(value) - rescue - false - end - - def handle_error(error) - # stop new jobs from posting - @error.value = error if @error_mode == :fail - @error_handler.call(self, error) - rescue - # do nothing - end - - def ns_find_last_job_for_thread - @queue.rindex { |job| job.caller == Thread.current.object_id } - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/array.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -require 'concurrent/utility/engine' -require 'concurrent/thread_safe/util' - -module Concurrent - - # @!macro concurrent_array - # - # A thread-safe subclass of Array. This version locks against the object - # itself for every method call, ensuring only one thread can be reading - # or writing at a time. This includes iteration methods like `#each`. - # - # @note `a += b` is **not** a **thread-safe** operation on - # `Concurrent::Array`. It reads array `a`, then it creates new `Concurrent::Array` - # which is concatenation of `a` and `b`, then it writes the concatenation to `a`. - # The read and write are independent operations they do not form a single atomic - # operation therefore when two `+=` operations are executed concurrently updates - # may be lost. Use `#concat` instead. - # - # @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array` - - # @!macro internal_implementation_note - ArrayImplementation = case - when Concurrent.on_cruby? - # Array is thread-safe in practice because CRuby runs - # threads one at a time and does not do context - # switching during the execution of C functions. - ::Array - - when Concurrent.on_jruby? - require 'jruby/synchronized' - - class JRubyArray < ::Array - include JRuby::Synchronized - end - JRubyArray - - when Concurrent.on_rbx? - require 'monitor' - require 'concurrent/thread_safe/util/data_structures' - - class RbxArray < ::Array - end - - ThreadSafe::Util.make_synchronized_on_rbx RbxArray - RbxArray - - when Concurrent.on_truffleruby? - require 'concurrent/thread_safe/util/data_structures' - - class TruffleRubyArray < ::Array - end - - ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyArray - TruffleRubyArray - - else - warn 'Possibly unsupported Ruby implementation' - ::Array - end - private_constant :ArrayImplementation - - # @!macro concurrent_array - class Array < ArrayImplementation - end - -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/async.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,459 +0,0 @@ -require 'concurrent/configuration' -require 'concurrent/ivar' -require 'concurrent/synchronization/lockable_object' - -module Concurrent - - # A mixin module that provides simple asynchronous behavior to a class, - # turning it into a simple actor. Loosely based on Erlang's - # [gen_server](http://www.erlang.org/doc/man/gen_server.html), but without - # supervision or linking. - # - # A more feature-rich {Concurrent::Actor} is also available when the - # capabilities of `Async` are too limited. - # - # ```cucumber - # Feature: - # As a stateful, plain old Ruby class - # I want safe, asynchronous behavior - # So my long-running methods don't block the main thread - # ``` - # - # The `Async` module is a way to mix simple yet powerful asynchronous - # capabilities into any plain old Ruby object or class, turning each object - # into a simple Actor. Method calls are processed on a background thread. The - # caller is free to perform other actions while processing occurs in the - # background. - # - # Method calls to the asynchronous object are made via two proxy methods: - # `async` (alias `cast`) and `await` (alias `call`). These proxy methods post - # the method call to the object's background thread and return a "future" - # which will eventually contain the result of the method call. - # - # This behavior is loosely patterned after Erlang's `gen_server` behavior. - # When an Erlang module implements the `gen_server` behavior it becomes - # inherently asynchronous. The `start` or `start_link` function spawns a - # process (similar to a thread but much more lightweight and efficient) and - # returns the ID of the process. Using the process ID, other processes can - # send messages to the `gen_server` via the `cast` and `call` methods. Unlike - # Erlang's `gen_server`, however, `Async` classes do not support linking or - # supervision trees. - # - # ## Basic Usage - # - # When this module is mixed into a class, objects of the class become inherently - # asynchronous. Each object gets its own background thread on which to post - # asynchronous method calls. Asynchronous method calls are executed in the - # background one at a time in the order they are received. - # - # To create an asynchronous class, simply mix in the `Concurrent::Async` module: - # - # ``` - # class Hello - # include Concurrent::Async - # - # def hello(name) - # "Hello, #{name}!" - # end - # end - # ``` - # - # When defining a constructor it is critical that the first line be a call to - # `super` with no arguments. The `super` method initializes the background - # thread and other asynchronous components. - # - # ``` - # class BackgroundLogger - # include Concurrent::Async - # - # def initialize(level) - # super() - # @logger = Logger.new(STDOUT) - # @logger.level = level - # end - # - # def info(msg) - # @logger.info(msg) - # end - # end - # ``` - # - # Mixing this module into a class provides each object two proxy methods: - # `async` and `await`. These methods are thread safe with respect to the - # enclosing object. The former proxy allows methods to be called - # asynchronously by posting to the object's internal thread. The latter proxy - # allows a method to be called synchronously but does so safely with respect - # to any pending asynchronous method calls and ensures proper ordering. Both - # methods return a {Concurrent::IVar} which can be inspected for the result - # of the proxied method call. Calling a method with `async` will return a - # `:pending` `IVar` whereas `await` will return a `:complete` `IVar`. - # - # ``` - # class Echo - # include Concurrent::Async - # - # def echo(msg) - # print "#{msg}\n" - # end - # end - # - # horn = Echo.new - # horn.echo('zero') # synchronous, not thread-safe - # # returns the actual return value of the method - # - # horn.async.echo('one') # asynchronous, non-blocking, thread-safe - # # returns an IVar in the :pending state - # - # horn.await.echo('two') # synchronous, blocking, thread-safe - # # returns an IVar in the :complete state - # ``` - # - # ## Let It Fail - # - # The `async` and `await` proxy methods have built-in error protection based - # on Erlang's famous "let it fail" philosophy. Instance methods should not be - # programmed defensively. When an exception is raised by a delegated method - # the proxy will rescue the exception, expose it to the caller as the `reason` - # attribute of the returned future, then process the next method call. - # - # ## Calling Methods Internally - # - # External method calls should *always* use the `async` and `await` proxy - # methods. When one method calls another method, the `async` proxy should - # rarely be used and the `await` proxy should *never* be used. - # - # When an object calls one of its own methods using the `await` proxy the - # second call will be enqueued *behind* the currently running method call. - # Any attempt to wait on the result will fail as the second call will never - # run until after the current call completes. - # - # Calling a method using the `await` proxy from within a method that was - # itself called using `async` or `await` will irreversibly deadlock the - # object. Do *not* do this, ever. - # - # ## Instance Variables and Attribute Accessors - # - # Instance variables do not need to be thread-safe so long as they are private. - # Asynchronous method calls are processed in the order they are received and - # are processed one at a time. Therefore private instance variables can only - # be accessed by one thread at a time. This is inherently thread-safe. - # - # When using private instance variables within asynchronous methods, the best - # practice is to read the instance variable into a local variable at the start - # of the method then update the instance variable at the *end* of the method. - # This way, should an exception be raised during method execution the internal - # state of the object will not have been changed. - # - # ### Reader Attributes - # - # The use of `attr_reader` is discouraged. Internal state exposed externally, - # when necessary, should be done through accessor methods. The instance - # variables exposed by these methods *must* be thread-safe, or they must be - # called using the `async` and `await` proxy methods. These two approaches are - # subtly different. - # - # When internal state is accessed via the `async` and `await` proxy methods, - # the returned value represents the object's state *at the time the call is - # processed*, which may *not* be the state of the object at the time the call - # is made. - # - # To get the state *at the current* time, irrespective of an enqueued method - # calls, a reader method must be called directly. This is inherently unsafe - # unless the instance variable is itself thread-safe, preferably using one - # of the thread-safe classes within this library. Because the thread-safe - # classes within this library are internally-locking or non-locking, they can - # be safely used from within asynchronous methods without causing deadlocks. - # - # Generally speaking, the best practice is to *not* expose internal state via - # reader methods. The best practice is to simply use the method's return value. - # - # ### Writer Attributes - # - # Writer attributes should never be used with asynchronous classes. Changing - # the state externally, even when done in the thread-safe way, is not logically - # consistent. Changes to state need to be timed with respect to all asynchronous - # method calls which my be in-process or enqueued. The only safe practice is to - # pass all necessary data to each method as arguments and let the method update - # the internal state as necessary. - # - # ## Class Constants, Variables, and Methods - # - # ### Class Constants - # - # Class constants do not need to be thread-safe. Since they are read-only and - # immutable they may be safely read both externally and from within - # asynchronous methods. - # - # ### Class Variables - # - # Class variables should be avoided. Class variables represent shared state. - # Shared state is anathema to concurrency. Should there be a need to share - # state using class variables they *must* be thread-safe, preferably - # using the thread-safe classes within this library. When updating class - # variables, never assign a new value/object to the variable itself. Assignment - # is not thread-safe in Ruby. Instead, use the thread-safe update functions - # of the variable itself to change the value. - # - # The best practice is to *never* use class variables with `Async` classes. - # - # ### Class Methods - # - # Class methods which are pure functions are safe. Class methods which modify - # class variables should be avoided, for all the reasons listed above. - # - # ## An Important Note About Thread Safe Guarantees - # - # > Thread safe guarantees can only be made when asynchronous method calls - # > are not mixed with direct method calls. Use only direct method calls - # > when the object is used exclusively on a single thread. Use only - # > `async` and `await` when the object is shared between threads. Once you - # > call a method using `async` or `await`, you should no longer call methods - # > directly on the object. Use `async` and `await` exclusively from then on. - # - # @example - # - # class Echo - # include Concurrent::Async - # - # def echo(msg) - # print "#{msg}\n" - # end - # end - # - # horn = Echo.new - # horn.echo('zero') # synchronous, not thread-safe - # # returns the actual return value of the method - # - # horn.async.echo('one') # asynchronous, non-blocking, thread-safe - # # returns an IVar in the :pending state - # - # horn.await.echo('two') # synchronous, blocking, thread-safe - # # returns an IVar in the :complete state - # - # @see Concurrent::Actor - # @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia - # @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server - # @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/ - module Async - - # @!method self.new(*args, &block) - # - # Instanciate a new object and ensure proper initialization of the - # synchronization mechanisms. - # - # @param [Array] args Zero or more arguments to be passed to the - # object's initializer. - # @param [Proc] block Optional block to pass to the object's initializer. - # @return [Object] A properly initialized object of the asynchronous class. - - # Check for the presence of a method on an object and determine if a given - # set of arguments matches the required arity. - # - # @param [Object] obj the object to check against - # @param [Symbol] method the method to check the object for - # @param [Array] args zero or more arguments for the arity check - # - # @raise [NameError] the object does not respond to `method` method - # @raise [ArgumentError] the given `args` do not match the arity of `method` - # - # @note This check is imperfect because of the way Ruby reports the arity of - # methods with a variable number of arguments. It is possible to determine - # if too few arguments are given but impossible to determine if too many - # arguments are given. This check may also fail to recognize dynamic behavior - # of the object, such as methods simulated with `method_missing`. - # - # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity - # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to? - # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing - # - # @!visibility private - def self.validate_argc(obj, method, *args) - argc = args.length - arity = obj.method(method).arity - - if arity >= 0 && argc != arity - raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})") - elsif arity < 0 && (arity = (arity + 1).abs) > argc - raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)") - end - end - - # @!visibility private - def self.included(base) - base.singleton_class.send(:alias_method, :original_new, :new) - base.extend(ClassMethods) - super(base) - end - - # @!visibility private - module ClassMethods - def new(*args, &block) - obj = original_new(*args, &block) - obj.send(:init_synchronization) - obj - end - end - private_constant :ClassMethods - - # Delegates asynchronous, thread-safe method calls to the wrapped object. - # - # @!visibility private - class AsyncDelegator < Synchronization::LockableObject - safe_initialization! - - # Create a new delegator object wrapping the given delegate. - # - # @param [Object] delegate the object to wrap and delegate method calls to - def initialize(delegate) - super() - @delegate = delegate - @queue = [] - @executor = Concurrent.global_io_executor - end - - # Delegates method calls to the wrapped object. - # - # @param [Symbol] method the method being called - # @param [Array] args zero or more arguments to the method - # - # @return [IVar] the result of the method call - # - # @raise [NameError] the object does not respond to `method` method - # @raise [ArgumentError] the given `args` do not match the arity of `method` - def method_missing(method, *args, &block) - super unless @delegate.respond_to?(method) - Async::validate_argc(@delegate, method, *args) - - ivar = Concurrent::IVar.new - synchronize do - @queue.push [ivar, method, args, block] - @executor.post { perform } if @queue.length == 1 - end - - ivar - end - - # Check whether the method is responsive - # - # @param [Symbol] method the method being called - def respond_to_missing?(method, include_private = false) - @delegate.respond_to?(method) || super - end - - # Perform all enqueued tasks. - # - # This method must be called from within the executor. It must not be - # called while already running. It will loop until the queue is empty. - def perform - loop do - ivar, method, args, block = synchronize { @queue.first } - break unless ivar # queue is empty - - begin - ivar.set(@delegate.send(method, *args, &block)) - rescue => error - ivar.fail(error) - end - - synchronize do - @queue.shift - return if @queue.empty? - end - end - end - end - private_constant :AsyncDelegator - - # Delegates synchronous, thread-safe method calls to the wrapped object. - # - # @!visibility private - class AwaitDelegator - - # Create a new delegator object wrapping the given delegate. - # - # @param [AsyncDelegator] delegate the object to wrap and delegate method calls to - def initialize(delegate) - @delegate = delegate - end - - # Delegates method calls to the wrapped object. - # - # @param [Symbol] method the method being called - # @param [Array] args zero or more arguments to the method - # - # @return [IVar] the result of the method call - # - # @raise [NameError] the object does not respond to `method` method - # @raise [ArgumentError] the given `args` do not match the arity of `method` - def method_missing(method, *args, &block) - ivar = @delegate.send(method, *args, &block) - ivar.wait - ivar - end - - # Check whether the method is responsive - # - # @param [Symbol] method the method being called - def respond_to_missing?(method, include_private = false) - @delegate.respond_to?(method) || super - end - end - private_constant :AwaitDelegator - - # Causes the chained method call to be performed asynchronously on the - # object's thread. The delegated method will return a future in the - # `:pending` state and the method call will have been scheduled on the - # object's thread. The final disposition of the method call can be obtained - # by inspecting the returned future. - # - # @!macro async_thread_safety_warning - # @note The method call is guaranteed to be thread safe with respect to - # all other method calls against the same object that are called with - # either `async` or `await`. The mutable nature of Ruby references - # (and object orientation in general) prevent any other thread safety - # guarantees. Do NOT mix direct method calls with delegated method calls. - # Use *only* delegated method calls when sharing the object between threads. - # - # @return [Concurrent::IVar] the pending result of the asynchronous operation - # - # @raise [NameError] the object does not respond to the requested method - # @raise [ArgumentError] the given `args` do not match the arity of - # the requested method - def async - @__async_delegator__ - end - alias_method :cast, :async - - # Causes the chained method call to be performed synchronously on the - # current thread. The delegated will return a future in either the - # `:fulfilled` or `:rejected` state and the delegated method will have - # completed. The final disposition of the delegated method can be obtained - # by inspecting the returned future. - # - # @!macro async_thread_safety_warning - # - # @return [Concurrent::IVar] the completed result of the synchronous operation - # - # @raise [NameError] the object does not respond to the requested method - # @raise [ArgumentError] the given `args` do not match the arity of the - # requested method - def await - @__await_delegator__ - end - alias_method :call, :await - - # Initialize the internal serializer and other stnchronization mechanisms. - # - # @note This method *must* be called immediately upon object construction. - # This is the only way thread-safe initialization can be guaranteed. - # - # @!visibility private - def init_synchronization - return self if defined?(@__async_initialized__) && @__async_initialized__ - @__async_initialized__ = true - @__async_delegator__ = AsyncDelegator.new(self) - @__await_delegator__ = AwaitDelegator.new(@__async_delegator__) - self - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atom.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ -require 'concurrent/atomic/atomic_reference' -require 'concurrent/collection/copy_on_notify_observer_set' -require 'concurrent/concern/observable' -require 'concurrent/synchronization' - -# @!macro thread_safe_variable_comparison -# -# ## Thread-safe Variable Classes -# -# Each of the thread-safe variable classes is designed to solve a different -# problem. In general: -# -# * *{Concurrent::Agent}:* Shared, mutable variable providing independent, -# uncoordinated, *asynchronous* change of individual values. Best used when -# the value will undergo frequent, complex updates. Suitable when the result -# of an update does not need to be known immediately. -# * *{Concurrent::Atom}:* Shared, mutable variable providing independent, -# uncoordinated, *synchronous* change of individual values. Best used when -# the value will undergo frequent reads but only occasional, though complex, -# updates. Suitable when the result of an update must be known immediately. -# * *{Concurrent::AtomicReference}:* A simple object reference that can be -# atomically. Updates are synchronous but fast. Best used when updates a -# simple set operations. Not suitable when updates are complex. -# {Concurrent::AtomicBoolean} and {Concurrent::AtomicFixnum} are similar -# but optimized for the given data type. -# * *{Concurrent::Exchanger}:* Shared, stateless synchronization point. Used -# when two or more threads need to exchange data. The threads will pair then -# block on each other until the exchange is complete. -# * *{Concurrent::MVar}:* Shared synchronization point. Used when one thread -# must give a value to another, which must take the value. The threads will -# block on each other until the exchange is complete. -# * *{Concurrent::ThreadLocalVar}:* Shared, mutable, isolated variable which -# holds a different value for each thread which has access. Often used as -# an instance variable in objects which must maintain different state -# for different threads. -# * *{Concurrent::TVar}:* Shared, mutable variables which provide -# *coordinated*, *synchronous*, change of *many* stated. Used when multiple -# value must change together, in an all-or-nothing transaction. - - -module Concurrent - - # Atoms provide a way to manage shared, synchronous, independent state. - # - # An atom is initialized with an initial value and an optional validation - # proc. At any time the value of the atom can be synchronously and safely - # changed. If a validator is given at construction then any new value - # will be checked against the validator and will be rejected if the - # validator returns false or raises an exception. - # - # There are two ways to change the value of an atom: {#compare_and_set} and - # {#swap}. The former will set the new value if and only if it validates and - # the current value matches the new value. The latter will atomically set the - # new value to the result of running the given block if and only if that - # value validates. - # - # ## Example - # - # ``` - # def next_fibonacci(set = nil) - # return [0, 1] if set.nil? - # set + [set[-2..-1].reduce{|sum,x| sum + x }] - # end - # - # # create an atom with an initial value - # atom = Concurrent::Atom.new(next_fibonacci) - # - # # send a few update requests - # 5.times do - # atom.swap{|set| next_fibonacci(set) } - # end - # - # # get the current value - # atom.value #=> [0, 1, 1, 2, 3, 5, 8] - # ``` - # - # ## Observation - # - # Atoms support observers through the {Concurrent::Observable} mixin module. - # Notification of observers occurs every time the value of the Atom changes. - # When notified the observer will receive three arguments: `time`, `old_value`, - # and `new_value`. The `time` argument is the time at which the value change - # occurred. The `old_value` is the value of the Atom when the change began - # The `new_value` is the value to which the Atom was set when the change - # completed. Note that `old_value` and `new_value` may be the same. This is - # not an error. It simply means that the change operation returned the same - # value. - # - # Unlike in Clojure, `Atom` cannot participate in {Concurrent::TVar} transactions. - # - # @!macro thread_safe_variable_comparison - # - # @see http://clojure.org/atoms Clojure Atoms - # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State - class Atom < Synchronization::Object - include Concern::Observable - - safe_initialization! - attr_atomic(:value) - private :value=, :swap_value, :compare_and_set_value, :update_value - public :value - alias_method :deref, :value - - # @!method value - # The current value of the atom. - # - # @return [Object] The current value. - - # Create a new atom with the given initial value. - # - # @param [Object] value The initial value - # @param [Hash] opts The options used to configure the atom - # @option opts [Proc] :validator (nil) Optional proc used to validate new - # values. It must accept one and only one argument which will be the - # intended new value. The validator will return true if the new value - # is acceptable else return false (preferrably) or raise an exception. - # - # @!macro deref_options - # - # @raise [ArgumentError] if the validator is not a `Proc` (when given) - def initialize(value, opts = {}) - super() - @Validator = opts.fetch(:validator, -> v { true }) - self.observers = Collection::CopyOnNotifyObserverSet.new - self.value = value - end - - # Atomically swaps the value of atom using the given block. The current - # value will be passed to the block, as will any arguments passed as - # arguments to the function. The new value will be validated against the - # (optional) validator proc given at construction. If validation fails the - # value will not be changed. - # - # Internally, {#swap} reads the current value, applies the block to it, and - # attempts to compare-and-set it in. Since another thread may have changed - # the value in the intervening time, it may have to retry, and does so in a - # spin loop. The net effect is that the value will always be the result of - # the application of the supplied block to a current value, atomically. - # However, because the block might be called multiple times, it must be free - # of side effects. - # - # @note The given block may be called multiple times, and thus should be free - # of side effects. - # - # @param [Object] args Zero or more arguments passed to the block. - # - # @yield [value, args] Calculates a new value for the atom based on the - # current value and any supplied arguments. - # @yieldparam value [Object] The current value of the atom. - # @yieldparam args [Object] All arguments passed to the function, in order. - # @yieldreturn [Object] The intended new value of the atom. - # - # @return [Object] The final value of the atom after all operations and - # validations are complete. - # - # @raise [ArgumentError] When no block is given. - def swap(*args) - raise ArgumentError.new('no block given') unless block_given? - - loop do - old_value = value - new_value = yield(old_value, *args) - begin - break old_value unless valid?(new_value) - break new_value if compare_and_set(old_value, new_value) - rescue - break old_value - end - end - end - - # Atomically sets the value of atom to the new value if and only if the - # current value of the atom is identical to the old value and the new - # value successfully validates against the (optional) validator given - # at construction. - # - # @param [Object] old_value The expected current value. - # @param [Object] new_value The intended new value. - # - # @return [Boolean] True if the value is changed else false. - def compare_and_set(old_value, new_value) - if valid?(new_value) && compare_and_set_value(old_value, new_value) - observers.notify_observers(Time.now, old_value, new_value) - true - else - false - end - end - - # Atomically sets the value of atom to the new value without regard for the - # current value so long as the new value successfully validates against the - # (optional) validator given at construction. - # - # @param [Object] new_value The intended new value. - # - # @return [Object] The final value of the atom after all operations and - # validations are complete. - def reset(new_value) - old_value = value - if valid?(new_value) - self.value = new_value - observers.notify_observers(Time.now, old_value, new_value) - new_value - else - old_value - end - end - - private - - # Is the new value valid? - # - # @param [Object] new_value The intended new value. - # @return [Boolean] false if the validator function returns false or raises - # an exception else true - def valid?(new_value) - @Validator.call(new_value) - rescue - false - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/abstract_thread_local_var.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -require 'concurrent/constants' - -module Concurrent - - # @!macro thread_local_var - # @!macro internal_implementation_note - # @!visibility private - class AbstractThreadLocalVar - - # @!macro thread_local_var_method_initialize - def initialize(default = nil, &default_block) - if default && block_given? - raise ArgumentError, "Cannot use both value and block as default value" - end - - if block_given? - @default_block = default_block - @default = nil - else - @default_block = nil - @default = default - end - - allocate_storage - end - - # @!macro thread_local_var_method_get - def value - raise NotImplementedError - end - - # @!macro thread_local_var_method_set - def value=(value) - raise NotImplementedError - end - - # @!macro thread_local_var_method_bind - def bind(value, &block) - if block_given? - old_value = self.value - begin - self.value = value - yield - ensure - self.value = old_value - end - end - end - - protected - - # @!visibility private - def allocate_storage - raise NotImplementedError - end - - # @!visibility private - def default - if @default_block - self.value = @default_block.call - else - @default - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_boolean.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -require 'concurrent/atomic/mutex_atomic_boolean' -require 'concurrent/synchronization' - -module Concurrent - - ################################################################### - - # @!macro atomic_boolean_method_initialize - # - # Creates a new `AtomicBoolean` with the given initial value. - # - # @param [Boolean] initial the initial value - - # @!macro atomic_boolean_method_value_get - # - # Retrieves the current `Boolean` value. - # - # @return [Boolean] the current value - - # @!macro atomic_boolean_method_value_set - # - # Explicitly sets the value. - # - # @param [Boolean] value the new value to be set - # - # @return [Boolean] the current value - - # @!macro atomic_boolean_method_true_question - # - # Is the current value `true` - # - # @return [Boolean] true if the current value is `true`, else false - - # @!macro atomic_boolean_method_false_question - # - # Is the current value `false` - # - # @return [Boolean] true if the current value is `false`, else false - - # @!macro atomic_boolean_method_make_true - # - # Explicitly sets the value to true. - # - # @return [Boolean] true is value has changed, otherwise false - - # @!macro atomic_boolean_method_make_false - # - # Explicitly sets the value to false. - # - # @return [Boolean] true is value has changed, otherwise false - - ################################################################### - - # @!macro atomic_boolean_public_api - # - # @!method initialize(initial = false) - # @!macro atomic_boolean_method_initialize - # - # @!method value - # @!macro atomic_boolean_method_value_get - # - # @!method value=(value) - # @!macro atomic_boolean_method_value_set - # - # @!method true? - # @!macro atomic_boolean_method_true_question - # - # @!method false? - # @!macro atomic_boolean_method_false_question - # - # @!method make_true - # @!macro atomic_boolean_method_make_true - # - # @!method make_false - # @!macro atomic_boolean_method_make_false - - ################################################################### - - # @!visibility private - # @!macro internal_implementation_note - AtomicBooleanImplementation = case - when defined?(JavaAtomicBoolean) - JavaAtomicBoolean - when defined?(CAtomicBoolean) - CAtomicBoolean - else - MutexAtomicBoolean - end - private_constant :AtomicBooleanImplementation - - # @!macro atomic_boolean - # - # A boolean value that can be updated atomically. Reads and writes to an atomic - # boolean and thread-safe and guaranteed to succeed. Reads and writes may block - # briefly but no explicit locking is required. - # - # @!macro thread_safe_variable_comparison - # - # Performance: - # - # ``` - # Testing with ruby 2.1.2 - # Testing with Concurrent::MutexAtomicBoolean... - # 2.790000 0.000000 2.790000 ( 2.791454) - # Testing with Concurrent::CAtomicBoolean... - # 0.740000 0.000000 0.740000 ( 0.740206) - # - # Testing with jruby 1.9.3 - # Testing with Concurrent::MutexAtomicBoolean... - # 5.240000 2.520000 7.760000 ( 3.683000) - # Testing with Concurrent::JavaAtomicBoolean... - # 3.340000 0.010000 3.350000 ( 0.855000) - # ``` - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html java.util.concurrent.atomic.AtomicBoolean - # - # @!macro atomic_boolean_public_api - class AtomicBoolean < AtomicBooleanImplementation - # @return [String] Short string representation. - def to_s - format '%s value:%s>', super[0..-2], value - end - - alias_method :inspect, :to_s - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_fixnum.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -require 'concurrent/atomic/mutex_atomic_fixnum' -require 'concurrent/synchronization' - -module Concurrent - - ################################################################### - - # @!macro atomic_fixnum_method_initialize - # - # Creates a new `AtomicFixnum` with the given initial value. - # - # @param [Fixnum] initial the initial value - # @raise [ArgumentError] if the initial value is not a `Fixnum` - - # @!macro atomic_fixnum_method_value_get - # - # Retrieves the current `Fixnum` value. - # - # @return [Fixnum] the current value - - # @!macro atomic_fixnum_method_value_set - # - # Explicitly sets the value. - # - # @param [Fixnum] value the new value to be set - # - # @return [Fixnum] the current value - # - # @raise [ArgumentError] if the new value is not a `Fixnum` - - # @!macro atomic_fixnum_method_increment - # - # Increases the current value by the given amount (defaults to 1). - # - # @param [Fixnum] delta the amount by which to increase the current value - # - # @return [Fixnum] the current value after incrementation - - # @!macro atomic_fixnum_method_decrement - # - # Decreases the current value by the given amount (defaults to 1). - # - # @param [Fixnum] delta the amount by which to decrease the current value - # - # @return [Fixnum] the current value after decrementation - - # @!macro atomic_fixnum_method_compare_and_set - # - # Atomically sets the value to the given updated value if the current - # value == the expected value. - # - # @param [Fixnum] expect the expected value - # @param [Fixnum] update the new value - # - # @return [Boolean] true if the value was updated else false - - # @!macro atomic_fixnum_method_update - # - # Pass the current value to the given block, replacing it - # with the block's result. May retry if the value changes - # during the block's execution. - # - # @yield [Object] Calculate a new value for the atomic reference using - # given (old) value - # @yieldparam [Object] old_value the starting value of the atomic reference - # - # @return [Object] the new value - - ################################################################### - - # @!macro atomic_fixnum_public_api - # - # @!method initialize(initial = 0) - # @!macro atomic_fixnum_method_initialize - # - # @!method value - # @!macro atomic_fixnum_method_value_get - # - # @!method value=(value) - # @!macro atomic_fixnum_method_value_set - # - # @!method increment(delta = 1) - # @!macro atomic_fixnum_method_increment - # - # @!method decrement(delta = 1) - # @!macro atomic_fixnum_method_decrement - # - # @!method compare_and_set(expect, update) - # @!macro atomic_fixnum_method_compare_and_set - # - # @!method update - # @!macro atomic_fixnum_method_update - - ################################################################### - - # @!visibility private - # @!macro internal_implementation_note - AtomicFixnumImplementation = case - when defined?(JavaAtomicFixnum) - JavaAtomicFixnum - when defined?(CAtomicFixnum) - CAtomicFixnum - else - MutexAtomicFixnum - end - private_constant :AtomicFixnumImplementation - - # @!macro atomic_fixnum - # - # A numeric value that can be updated atomically. Reads and writes to an atomic - # fixnum and thread-safe and guaranteed to succeed. Reads and writes may block - # briefly but no explicit locking is required. - # - # @!macro thread_safe_variable_comparison - # - # Performance: - # - # ``` - # Testing with ruby 2.1.2 - # Testing with Concurrent::MutexAtomicFixnum... - # 3.130000 0.000000 3.130000 ( 3.136505) - # Testing with Concurrent::CAtomicFixnum... - # 0.790000 0.000000 0.790000 ( 0.785550) - # - # Testing with jruby 1.9.3 - # Testing with Concurrent::MutexAtomicFixnum... - # 5.460000 2.460000 7.920000 ( 3.715000) - # Testing with Concurrent::JavaAtomicFixnum... - # 4.520000 0.030000 4.550000 ( 1.187000) - # ``` - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html java.util.concurrent.atomic.AtomicLong - # - # @!macro atomic_fixnum_public_api - class AtomicFixnum < AtomicFixnumImplementation - # @return [String] Short string representation. - def to_s - format '%s value:%s>', super[0..-2], value - end - - alias_method :inspect, :to_s - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_markable_reference.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -module Concurrent - # An atomic reference which maintains an object reference along with a mark bit - # that can be updated atomically. - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html - # java.util.concurrent.atomic.AtomicMarkableReference - class AtomicMarkableReference < ::Concurrent::Synchronization::Object - - attr_atomic(:reference) - private :reference, :reference=, :swap_reference, :compare_and_set_reference, :update_reference - - def initialize(value = nil, mark = false) - super() - self.reference = immutable_array(value, mark) - end - - # Atomically sets the value and mark to the given updated value and - # mark given both: - # - the current value == the expected value && - # - the current mark == the expected mark - # - # @param [Object] expected_val the expected value - # @param [Object] new_val the new value - # @param [Boolean] expected_mark the expected mark - # @param [Boolean] new_mark the new mark - # - # @return [Boolean] `true` if successful. A `false` return indicates - # that the actual value was not equal to the expected value or the - # actual mark was not equal to the expected mark - def compare_and_set(expected_val, new_val, expected_mark, new_mark) - # Memoize a valid reference to the current AtomicReference for - # later comparison. - current = reference - curr_val, curr_mark = current - - # Ensure that that the expected marks match. - return false unless expected_mark == curr_mark - - if expected_val.is_a? Numeric - # If the object is a numeric, we need to ensure we are comparing - # the numerical values - return false unless expected_val == curr_val - else - # Otherwise, we need to ensure we are comparing the object identity. - # Theoretically, this could be incorrect if a user monkey-patched - # `Object#equal?`, but they should know that they are playing with - # fire at that point. - return false unless expected_val.equal? curr_val - end - - prospect = immutable_array(new_val, new_mark) - - compare_and_set_reference current, prospect - end - - alias_method :compare_and_swap, :compare_and_set - - # Gets the current reference and marked values. - # - # @return [Array] the current reference and marked values - def get - reference - end - - # Gets the current value of the reference - # - # @return [Object] the current value of the reference - def value - reference[0] - end - - # Gets the current marked value - # - # @return [Boolean] the current marked value - def mark - reference[1] - end - - alias_method :marked?, :mark - - # _Unconditionally_ sets to the given value of both the reference and - # the mark. - # - # @param [Object] new_val the new value - # @param [Boolean] new_mark the new mark - # - # @return [Array] both the new value and the new mark - def set(new_val, new_mark) - self.reference = immutable_array(new_val, new_mark) - end - - # Pass the current value and marked state to the given block, replacing it - # with the block's results. May retry if the value changes during the - # block's execution. - # - # @yield [Object] Calculate a new value and marked state for the atomic - # reference using given (old) value and (old) marked - # @yieldparam [Object] old_val the starting value of the atomic reference - # @yieldparam [Boolean] old_mark the starting state of marked - # - # @return [Array] the new value and new mark - def update - loop do - old_val, old_mark = reference - new_val, new_mark = yield old_val, old_mark - - if compare_and_set old_val, new_val, old_mark, new_mark - return immutable_array(new_val, new_mark) - end - end - end - - # Pass the current value to the given block, replacing it - # with the block's result. Raise an exception if the update - # fails. - # - # @yield [Object] Calculate a new value and marked state for the atomic - # reference using given (old) value and (old) marked - # @yieldparam [Object] old_val the starting value of the atomic reference - # @yieldparam [Boolean] old_mark the starting state of marked - # - # @return [Array] the new value and marked state - # - # @raise [Concurrent::ConcurrentUpdateError] if the update fails - def try_update! - old_val, old_mark = reference - new_val, new_mark = yield old_val, old_mark - - unless compare_and_set old_val, new_val, old_mark, new_mark - fail ::Concurrent::ConcurrentUpdateError, - 'AtomicMarkableReference: Update failed due to race condition.', - 'Note: If you would like to guarantee an update, please use ' + - 'the `AtomicMarkableReference#update` method.' - end - - immutable_array(new_val, new_mark) - end - - # Pass the current value to the given block, replacing it with the - # block's result. Simply return nil if update fails. - # - # @yield [Object] Calculate a new value and marked state for the atomic - # reference using given (old) value and (old) marked - # @yieldparam [Object] old_val the starting value of the atomic reference - # @yieldparam [Boolean] old_mark the starting state of marked - # - # @return [Array] the new value and marked state, or nil if - # the update failed - def try_update - old_val, old_mark = reference - new_val, new_mark = yield old_val, old_mark - - return unless compare_and_set old_val, new_val, old_mark, new_mark - - immutable_array(new_val, new_mark) - end - - private - - def immutable_array(*args) - args.freeze - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/atomic_reference.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,204 +0,0 @@ -require 'concurrent/synchronization' -require 'concurrent/utility/engine' -require 'concurrent/atomic_reference/numeric_cas_wrapper' - -# Shim for TruffleRuby::AtomicReference -if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference) - # @!visibility private - module TruffleRuby - AtomicReference = Truffle::AtomicReference - end -end - -module Concurrent - - # Define update methods that use direct paths - # - # @!visibility private - # @!macro internal_implementation_note - module AtomicDirectUpdate - - # @!macro atomic_reference_method_update - # - # Pass the current value to the given block, replacing it - # with the block's result. May retry if the value changes - # during the block's execution. - # - # @yield [Object] Calculate a new value for the atomic reference using - # given (old) value - # @yieldparam [Object] old_value the starting value of the atomic reference - # @return [Object] the new value - def update - true until compare_and_set(old_value = get, new_value = yield(old_value)) - new_value - end - - # @!macro atomic_reference_method_try_update - # - # Pass the current value to the given block, replacing it - # with the block's result. Return nil if the update fails. - # - # @yield [Object] Calculate a new value for the atomic reference using - # given (old) value - # @yieldparam [Object] old_value the starting value of the atomic reference - # @note This method was altered to avoid raising an exception by default. - # Instead, this method now returns `nil` in case of failure. For more info, - # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336 - # @return [Object] the new value, or nil if update failed - def try_update - old_value = get - new_value = yield old_value - - return unless compare_and_set old_value, new_value - - new_value - end - - # @!macro atomic_reference_method_try_update! - # - # Pass the current value to the given block, replacing it - # with the block's result. Raise an exception if the update - # fails. - # - # @yield [Object] Calculate a new value for the atomic reference using - # given (old) value - # @yieldparam [Object] old_value the starting value of the atomic reference - # @note This behavior mimics the behavior of the original - # `AtomicReference#try_update` API. The reason this was changed was to - # avoid raising exceptions (which are inherently slow) by default. For more - # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336 - # @return [Object] the new value - # @raise [Concurrent::ConcurrentUpdateError] if the update fails - def try_update! - old_value = get - new_value = yield old_value - unless compare_and_set(old_value, new_value) - if $VERBOSE - raise ConcurrentUpdateError, "Update failed" - else - raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE - end - end - new_value - end - end - - require 'concurrent/atomic_reference/mutex_atomic' - - # @!macro atomic_reference - # - # An object reference that may be updated atomically. All read and write - # operations have java volatile semantic. - # - # @!macro thread_safe_variable_comparison - # - # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html - # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html - # - # @!method initialize(value = nil) - # @!macro atomic_reference_method_initialize - # @param [Object] value The initial value. - # - # @!method get - # @!macro atomic_reference_method_get - # Gets the current value. - # @return [Object] the current value - # - # @!method set(new_value) - # @!macro atomic_reference_method_set - # Sets to the given value. - # @param [Object] new_value the new value - # @return [Object] the new value - # - # @!method get_and_set(new_value) - # @!macro atomic_reference_method_get_and_set - # Atomically sets to the given value and returns the old value. - # @param [Object] new_value the new value - # @return [Object] the old value - # - # @!method compare_and_set(old_value, new_value) - # @!macro atomic_reference_method_compare_and_set - # - # Atomically sets the value to the given updated value if - # the current value == the expected value. - # - # @param [Object] old_value the expected value - # @param [Object] new_value the new value - # - # @return [Boolean] `true` if successful. A `false` return indicates - # that the actual value was not equal to the expected value. - # - # @!method update - # @!macro atomic_reference_method_update - # - # @!method try_update - # @!macro atomic_reference_method_try_update - # - # @!method try_update! - # @!macro atomic_reference_method_try_update! - - - # @!macro internal_implementation_note - class ConcurrentUpdateError < ThreadError - # frozen pre-allocated backtrace to speed ConcurrentUpdateError - CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze - end - - # @!macro internal_implementation_note - AtomicReferenceImplementation = case - when Concurrent.on_cruby? && Concurrent.c_extensions_loaded? - # @!visibility private - # @!macro internal_implementation_note - class CAtomicReference - include AtomicDirectUpdate - include AtomicNumericCompareAndSetWrapper - alias_method :compare_and_swap, :compare_and_set - end - CAtomicReference - when Concurrent.on_jruby? - # @!visibility private - # @!macro internal_implementation_note - class JavaAtomicReference - include AtomicDirectUpdate - end - JavaAtomicReference - when Concurrent.on_truffleruby? - class TruffleRubyAtomicReference < TruffleRuby::AtomicReference - include AtomicDirectUpdate - alias_method :value, :get - alias_method :value=, :set - alias_method :compare_and_swap, :compare_and_set - alias_method :swap, :get_and_set - end - when Concurrent.on_rbx? - # @note Extends `Rubinius::AtomicReference` version adding aliases - # and numeric logic. - # - # @!visibility private - # @!macro internal_implementation_note - class RbxAtomicReference < Rubinius::AtomicReference - alias_method :_compare_and_set, :compare_and_set - include AtomicDirectUpdate - include AtomicNumericCompareAndSetWrapper - alias_method :value, :get - alias_method :value=, :set - alias_method :swap, :get_and_set - alias_method :compare_and_swap, :compare_and_set - end - RbxAtomicReference - else - MutexAtomicReference - end - private_constant :AtomicReferenceImplementation - - # @!macro atomic_reference - class AtomicReference < AtomicReferenceImplementation - - # @return [String] Short string representation. - def to_s - format '%s value:%s>', super[0..-2], get - end - - alias_method :inspect, :to_s - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/count_down_latch.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -require 'concurrent/atomic/mutex_count_down_latch' -require 'concurrent/atomic/java_count_down_latch' -require 'concurrent/utility/engine' - -module Concurrent - - ################################################################### - - # @!macro count_down_latch_method_initialize - # - # Create a new `CountDownLatch` with the initial `count`. - # - # @param [new] count the initial count - # - # @raise [ArgumentError] if `count` is not an integer or is less than zero - - # @!macro count_down_latch_method_wait - # - # Block on the latch until the counter reaches zero or until `timeout` is reached. - # - # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` - # to block indefinitely - # @return [Boolean] `true` if the `count` reaches zero else false on `timeout` - - # @!macro count_down_latch_method_count_down - # - # Signal the latch to decrement the counter. Will signal all blocked threads when - # the `count` reaches zero. - - # @!macro count_down_latch_method_count - # - # The current value of the counter. - # - # @return [Fixnum] the current value of the counter - - ################################################################### - - # @!macro count_down_latch_public_api - # - # @!method initialize(count = 1) - # @!macro count_down_latch_method_initialize - # - # @!method wait(timeout = nil) - # @!macro count_down_latch_method_wait - # - # @!method count_down - # @!macro count_down_latch_method_count_down - # - # @!method count - # @!macro count_down_latch_method_count - - ################################################################### - - # @!visibility private - # @!macro internal_implementation_note - CountDownLatchImplementation = case - when Concurrent.on_jruby? - JavaCountDownLatch - else - MutexCountDownLatch - end - private_constant :CountDownLatchImplementation - - # @!macro count_down_latch - # - # A synchronization object that allows one thread to wait on multiple other threads. - # The thread that will wait creates a `CountDownLatch` and sets the initial value - # (normally equal to the number of other threads). The initiating thread passes the - # latch to the other threads then waits for the other threads by calling the `#wait` - # method. Each of the other threads calls `#count_down` when done with its work. - # When the latch counter reaches zero the waiting thread is unblocked and continues - # with its work. A `CountDownLatch` can be used only once. Its value cannot be reset. - # - # @!macro count_down_latch_public_api - # @example Waiter and Decrementer - # latch = Concurrent::CountDownLatch.new(3) - # - # waiter = Thread.new do - # latch.wait() - # puts ("Waiter released") - # end - # - # decrementer = Thread.new do - # sleep(1) - # latch.count_down - # puts latch.count - # - # sleep(1) - # latch.count_down - # puts latch.count - # - # sleep(1) - # latch.count_down - # puts latch.count - # end - # - # [waiter, decrementer].each(&:join) - class CountDownLatch < CountDownLatchImplementation - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/cyclic_barrier.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -require 'concurrent/synchronization' -require 'concurrent/utility/native_integer' - -module Concurrent - - # A synchronization aid that allows a set of threads to all wait for each - # other to reach a common barrier point. - # @example - # barrier = Concurrent::CyclicBarrier.new(3) - # jobs = Array.new(3) { |i| -> { sleep i; p done: i } } - # process = -> (i) do - # # waiting to start at the same time - # barrier.wait - # # execute job - # jobs[i].call - # # wait for others to finish - # barrier.wait - # end - # threads = 2.times.map do |i| - # Thread.new(i, &process) - # end - # - # # use main as well - # process.call 2 - # - # # here we can be sure that all jobs are processed - class CyclicBarrier < Synchronization::LockableObject - - # @!visibility private - Generation = Struct.new(:status) - private_constant :Generation - - # Create a new `CyclicBarrier` that waits for `parties` threads - # - # @param [Fixnum] parties the number of parties - # @yield an optional block that will be executed that will be executed after - # the last thread arrives and before the others are released - # - # @raise [ArgumentError] if `parties` is not an integer or is less than zero - def initialize(parties, &block) - Utility::NativeInteger.ensure_integer_and_bounds parties - Utility::NativeInteger.ensure_positive_and_no_zero parties - - super(&nil) - synchronize { ns_initialize parties, &block } - end - - # @return [Fixnum] the number of threads needed to pass the barrier - def parties - synchronize { @parties } - end - - # @return [Fixnum] the number of threads currently waiting on the barrier - def number_waiting - synchronize { @number_waiting } - end - - # Blocks on the barrier until the number of waiting threads is equal to - # `parties` or until `timeout` is reached or `reset` is called - # If a block has been passed to the constructor, it will be executed once by - # the last arrived thread before releasing the others - # @param [Fixnum] timeout the number of seconds to wait for the counter or - # `nil` to block indefinitely - # @return [Boolean] `true` if the `count` reaches zero else false on - # `timeout` or on `reset` or if the barrier is broken - def wait(timeout = nil) - synchronize do - - return false unless @generation.status == :waiting - - @number_waiting += 1 - - if @number_waiting == @parties - @action.call if @action - ns_generation_done @generation, :fulfilled - true - else - generation = @generation - if ns_wait_until(timeout) { generation.status != :waiting } - generation.status == :fulfilled - else - ns_generation_done generation, :broken, false - false - end - end - end - end - - # resets the barrier to its initial state - # If there is at least one waiting thread, it will be woken up, the `wait` - # method will return false and the barrier will be broken - # If the barrier is broken, this method restores it to the original state - # - # @return [nil] - def reset - synchronize { ns_generation_done @generation, :reset } - end - - # A barrier can be broken when: - # - a thread called the `reset` method while at least one other thread was waiting - # - at least one thread timed out on `wait` method - # - # A broken barrier can be restored using `reset` it's safer to create a new one - # @return [Boolean] true if the barrier is broken otherwise false - def broken? - synchronize { @generation.status != :waiting } - end - - protected - - def ns_generation_done(generation, status, continue = true) - generation.status = status - ns_next_generation if continue - ns_broadcast - end - - def ns_next_generation - @generation = Generation.new(:waiting) - @number_waiting = 0 - end - - def ns_initialize(parties, &block) - @parties = parties - @action = block - ns_next_generation - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/event.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -require 'thread' -require 'concurrent/synchronization' - -module Concurrent - - # Old school kernel-style event reminiscent of Win32 programming in C++. - # - # When an `Event` is created it is in the `unset` state. Threads can choose to - # `#wait` on the event, blocking until released by another thread. When one - # thread wants to alert all blocking threads it calls the `#set` method which - # will then wake up all listeners. Once an `Event` has been set it remains set. - # New threads calling `#wait` will return immediately. An `Event` may be - # `#reset` at any time once it has been set. - # - # @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx - # @example - # event = Concurrent::Event.new - # - # t1 = Thread.new do - # puts "t1 is waiting" - # event.wait(1) - # puts "event ocurred" - # end - # - # t2 = Thread.new do - # puts "t2 calling set" - # event.set - # end - # - # [t1, t2].each(&:join) - # - # # prints: - # # t2 calling set - # # t1 is waiting - # # event occurred - class Event < Synchronization::LockableObject - - # Creates a new `Event` in the unset state. Threads calling `#wait` on the - # `Event` will block. - def initialize - super - synchronize { ns_initialize } - end - - # Is the object in the set state? - # - # @return [Boolean] indicating whether or not the `Event` has been set - def set? - synchronize { @set } - end - - # Trigger the event, setting the state to `set` and releasing all threads - # waiting on the event. Has no effect if the `Event` has already been set. - # - # @return [Boolean] should always return `true` - def set - synchronize { ns_set } - end - - def try? - synchronize { @set ? false : ns_set } - end - - # Reset a previously set event back to the `unset` state. - # Has no effect if the `Event` has not yet been set. - # - # @return [Boolean] should always return `true` - def reset - synchronize do - if @set - @set = false - @iteration +=1 - end - true - end - end - - # Wait a given number of seconds for the `Event` to be set by another - # thread. Will wait forever when no `timeout` value is given. Returns - # immediately if the `Event` has already been set. - # - # @return [Boolean] true if the `Event` was set before timeout else false - def wait(timeout = nil) - synchronize do - unless @set - iteration = @iteration - ns_wait_until(timeout) { iteration < @iteration || @set } - else - true - end - end - end - - protected - - def ns_set - unless @set - @set = true - ns_broadcast - end - true - end - - def ns_initialize - @set = false - @iteration = 0 - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_count_down_latch.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -if Concurrent.on_jruby? - - module Concurrent - - # @!macro count_down_latch - # @!visibility private - # @!macro internal_implementation_note - class JavaCountDownLatch - - # @!macro count_down_latch_method_initialize - def initialize(count = 1) - Utility::NativeInteger.ensure_integer_and_bounds(count) - Utility::NativeInteger.ensure_positive(count) - @latch = java.util.concurrent.CountDownLatch.new(count) - end - - # @!macro count_down_latch_method_wait - def wait(timeout = nil) - result = nil - if timeout.nil? - Synchronization::JRuby.sleep_interruptibly { @latch.await } - result = true - else - Synchronization::JRuby.sleep_interruptibly do - result = @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) - end - end - result - end - - # @!macro count_down_latch_method_count_down - def count_down - @latch.countDown - end - - # @!macro count_down_latch_method_count - def count - @latch.getCount - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/java_thread_local_var.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -require 'concurrent/atomic/abstract_thread_local_var' - -if Concurrent.on_jruby? - - module Concurrent - - # @!visibility private - # @!macro internal_implementation_note - class JavaThreadLocalVar < AbstractThreadLocalVar - - # @!macro thread_local_var_method_get - def value - value = @var.get - - if value.nil? - default - elsif value == NULL - nil - else - value - end - end - - # @!macro thread_local_var_method_set - def value=(value) - @var.set(value) - end - - protected - - # @!visibility private - def allocate_storage - @var = java.lang.ThreadLocal.new - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_boolean.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -require 'concurrent/synchronization' - -module Concurrent - - # @!macro atomic_boolean - # @!visibility private - # @!macro internal_implementation_note - class MutexAtomicBoolean < Synchronization::LockableObject - - # @!macro atomic_boolean_method_initialize - def initialize(initial = false) - super() - synchronize { ns_initialize(initial) } - end - - # @!macro atomic_boolean_method_value_get - def value - synchronize { @value } - end - - # @!macro atomic_boolean_method_value_set - def value=(value) - synchronize { @value = !!value } - end - - # @!macro atomic_boolean_method_true_question - def true? - synchronize { @value } - end - - # @!macro atomic_boolean_method_false_question - def false? - synchronize { !@value } - end - - # @!macro atomic_boolean_method_make_true - def make_true - synchronize { ns_make_value(true) } - end - - # @!macro atomic_boolean_method_make_false - def make_false - synchronize { ns_make_value(false) } - end - - protected - - # @!visibility private - def ns_initialize(initial) - @value = !!initial - end - - private - - # @!visibility private - def ns_make_value(value) - old = @value - @value = value - old != @value - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_atomic_fixnum.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -require 'concurrent/synchronization' -require 'concurrent/utility/native_integer' - -module Concurrent - - # @!macro atomic_fixnum - # @!visibility private - # @!macro internal_implementation_note - class MutexAtomicFixnum < Synchronization::LockableObject - - # @!macro atomic_fixnum_method_initialize - def initialize(initial = 0) - super() - synchronize { ns_initialize(initial) } - end - - # @!macro atomic_fixnum_method_value_get - def value - synchronize { @value } - end - - # @!macro atomic_fixnum_method_value_set - def value=(value) - synchronize { ns_set(value) } - end - - # @!macro atomic_fixnum_method_increment - def increment(delta = 1) - synchronize { ns_set(@value + delta.to_i) } - end - - alias_method :up, :increment - - # @!macro atomic_fixnum_method_decrement - def decrement(delta = 1) - synchronize { ns_set(@value - delta.to_i) } - end - - alias_method :down, :decrement - - # @!macro atomic_fixnum_method_compare_and_set - def compare_and_set(expect, update) - synchronize do - if @value == expect.to_i - @value = update.to_i - true - else - false - end - end - end - - # @!macro atomic_fixnum_method_update - def update - synchronize do - @value = yield @value - end - end - - protected - - # @!visibility private - def ns_initialize(initial) - ns_set(initial) - end - - private - - # @!visibility private - def ns_set(value) - Utility::NativeInteger.ensure_integer_and_bounds value - @value = value - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_count_down_latch.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -require 'concurrent/synchronization' -require 'concurrent/utility/native_integer' - -module Concurrent - - # @!macro count_down_latch - # @!visibility private - # @!macro internal_implementation_note - class MutexCountDownLatch < Synchronization::LockableObject - - # @!macro count_down_latch_method_initialize - def initialize(count = 1) - Utility::NativeInteger.ensure_integer_and_bounds count - Utility::NativeInteger.ensure_positive count - - super() - synchronize { ns_initialize count } - end - - # @!macro count_down_latch_method_wait - def wait(timeout = nil) - synchronize { ns_wait_until(timeout) { @count == 0 } } - end - - # @!macro count_down_latch_method_count_down - def count_down - synchronize do - @count -= 1 if @count > 0 - ns_broadcast if @count == 0 - end - end - - # @!macro count_down_latch_method_count - def count - synchronize { @count } - end - - protected - - def ns_initialize(count) - @count = count - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/mutex_semaphore.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -require 'concurrent/synchronization' -require 'concurrent/utility/native_integer' - -module Concurrent - - # @!macro semaphore - # @!visibility private - # @!macro internal_implementation_note - class MutexSemaphore < Synchronization::LockableObject - - # @!macro semaphore_method_initialize - def initialize(count) - Utility::NativeInteger.ensure_integer_and_bounds count - - super() - synchronize { ns_initialize count } - end - - # @!macro semaphore_method_acquire - def acquire(permits = 1) - Utility::NativeInteger.ensure_integer_and_bounds permits - Utility::NativeInteger.ensure_positive permits - - synchronize do - try_acquire_timed(permits, nil) - nil - end - end - - # @!macro semaphore_method_available_permits - def available_permits - synchronize { @free } - end - - # @!macro semaphore_method_drain_permits - # - # Acquires and returns all permits that are immediately available. - # - # @return [Integer] - def drain_permits - synchronize do - @free.tap { |_| @free = 0 } - end - end - - # @!macro semaphore_method_try_acquire - def try_acquire(permits = 1, timeout = nil) - Utility::NativeInteger.ensure_integer_and_bounds permits - Utility::NativeInteger.ensure_positive permits - - synchronize do - if timeout.nil? - try_acquire_now(permits) - else - try_acquire_timed(permits, timeout) - end - end - end - - # @!macro semaphore_method_release - def release(permits = 1) - Utility::NativeInteger.ensure_integer_and_bounds permits - Utility::NativeInteger.ensure_positive permits - - synchronize do - @free += permits - permits.times { ns_signal } - end - nil - end - - # Shrinks the number of available permits by the indicated reduction. - # - # @param [Fixnum] reduction Number of permits to remove. - # - # @raise [ArgumentError] if `reduction` is not an integer or is negative - # - # @raise [ArgumentError] if `@free` - `@reduction` is less than zero - # - # @return [nil] - # - # @!visibility private - def reduce_permits(reduction) - Utility::NativeInteger.ensure_integer_and_bounds reduction - Utility::NativeInteger.ensure_positive reduction - - synchronize { @free -= reduction } - nil - end - - protected - - # @!visibility private - def ns_initialize(count) - @free = count - end - - private - - # @!visibility private - def try_acquire_now(permits) - if @free >= permits - @free -= permits - true - else - false - end - end - - # @!visibility private - def try_acquire_timed(permits, timeout) - ns_wait_until(timeout) { try_acquire_now(permits) } - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/read_write_lock.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,254 +0,0 @@ -require 'thread' -require 'concurrent/atomic/atomic_fixnum' -require 'concurrent/errors' -require 'concurrent/synchronization' - -module Concurrent - - # Ruby read-write lock implementation - # - # Allows any number of concurrent readers, but only one concurrent writer - # (And if the "write" lock is taken, any readers who come along will have to wait) - # - # If readers are already active when a writer comes along, the writer will wait for - # all the readers to finish before going ahead. - # Any additional readers that come when the writer is already waiting, will also - # wait (so writers are not starved). - # - # This implementation is based on `java.util.concurrent.ReentrantReadWriteLock`. - # - # @example - # lock = Concurrent::ReadWriteLock.new - # lock.with_read_lock { data.retrieve } - # lock.with_write_lock { data.modify! } - # - # @note Do **not** try to acquire the write lock while already holding a read lock - # **or** try to acquire the write lock while you already have it. - # This will lead to deadlock - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock - class ReadWriteLock < Synchronization::Object - - # @!visibility private - WAITING_WRITER = 1 << 15 - - # @!visibility private - RUNNING_WRITER = 1 << 29 - - # @!visibility private - MAX_READERS = WAITING_WRITER - 1 - - # @!visibility private - MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 - - safe_initialization! - - # Implementation notes: - # A goal is to make the uncontended path for both readers/writers lock-free - # Only if there is reader-writer or writer-writer contention, should locks be used - # Internal state is represented by a single integer ("counter"), and updated - # using atomic compare-and-swap operations - # When the counter is 0, the lock is free - # Each reader increments the counter by 1 when acquiring a read lock - # (and decrements by 1 when releasing the read lock) - # The counter is increased by (1 << 15) for each writer waiting to acquire the - # write lock, and by (1 << 29) if the write lock is taken - - # Create a new `ReadWriteLock` in the unlocked state. - def initialize - super() - @Counter = AtomicFixnum.new(0) # single integer which represents lock state - @ReadLock = Synchronization::Lock.new - @WriteLock = Synchronization::Lock.new - end - - # Execute a block operation within a read lock. - # - # @yield the task to be performed within the lock. - # - # @return [Object] the result of the block operation. - # - # @raise [ArgumentError] when no block is given. - # @raise [Concurrent::ResourceLimitError] if the maximum number of readers - # is exceeded. - def with_read_lock - raise ArgumentError.new('no block given') unless block_given? - acquire_read_lock - begin - yield - ensure - release_read_lock - end - end - - # Execute a block operation within a write lock. - # - # @yield the task to be performed within the lock. - # - # @return [Object] the result of the block operation. - # - # @raise [ArgumentError] when no block is given. - # @raise [Concurrent::ResourceLimitError] if the maximum number of readers - # is exceeded. - def with_write_lock - raise ArgumentError.new('no block given') unless block_given? - acquire_write_lock - begin - yield - ensure - release_write_lock - end - end - - # Acquire a read lock. If a write lock has been acquired will block until - # it is released. Will not block if other read locks have been acquired. - # - # @return [Boolean] true if the lock is successfully acquired - # - # @raise [Concurrent::ResourceLimitError] if the maximum number of readers - # is exceeded. - def acquire_read_lock - while true - c = @Counter.value - raise ResourceLimitError.new('Too many reader threads') if max_readers?(c) - - # If a writer is waiting when we first queue up, we need to wait - if waiting_writer?(c) - @ReadLock.wait_until { !waiting_writer? } - - # after a reader has waited once, they are allowed to "barge" ahead of waiting writers - # but if a writer is *running*, the reader still needs to wait (naturally) - while true - c = @Counter.value - if running_writer?(c) - @ReadLock.wait_until { !running_writer? } - else - return if @Counter.compare_and_set(c, c+1) - end - end - else - break if @Counter.compare_and_set(c, c+1) - end - end - true - end - - # Release a previously acquired read lock. - # - # @return [Boolean] true if the lock is successfully released - def release_read_lock - while true - c = @Counter.value - if @Counter.compare_and_set(c, c-1) - # If one or more writers were waiting, and we were the last reader, wake a writer up - if waiting_writer?(c) && running_readers(c) == 1 - @WriteLock.signal - end - break - end - end - true - end - - # Acquire a write lock. Will block and wait for all active readers and writers. - # - # @return [Boolean] true if the lock is successfully acquired - # - # @raise [Concurrent::ResourceLimitError] if the maximum number of writers - # is exceeded. - def acquire_write_lock - while true - c = @Counter.value - raise ResourceLimitError.new('Too many writer threads') if max_writers?(c) - - if c == 0 # no readers OR writers running - # if we successfully swap the RUNNING_WRITER bit on, then we can go ahead - break if @Counter.compare_and_set(0, RUNNING_WRITER) - elsif @Counter.compare_and_set(c, c+WAITING_WRITER) - while true - # Now we have successfully incremented, so no more readers will be able to increment - # (they will wait instead) - # However, readers OR writers could decrement right here, OR another writer could increment - @WriteLock.wait_until do - # So we have to do another check inside the synchronized section - # If a writer OR reader is running, then go to sleep - c = @Counter.value - !running_writer?(c) && !running_readers?(c) - end - - # We just came out of a wait - # If we successfully turn the RUNNING_WRITER bit on with an atomic swap, - # Then we are OK to stop waiting and go ahead - # Otherwise go back and wait again - c = @Counter.value - break if !running_writer?(c) && !running_readers?(c) && @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER) - end - break - end - end - true - end - - # Release a previously acquired write lock. - # - # @return [Boolean] true if the lock is successfully released - def release_write_lock - return true unless running_writer? - c = @Counter.update { |counter| counter - RUNNING_WRITER } - @ReadLock.broadcast - @WriteLock.signal if waiting_writers(c) > 0 - true - end - - # Queries if the write lock is held by any thread. - # - # @return [Boolean] true if the write lock is held else false` - def write_locked? - @Counter.value >= RUNNING_WRITER - end - - # Queries whether any threads are waiting to acquire the read or write lock. - # - # @return [Boolean] true if any threads are waiting for a lock else false - def has_waiters? - waiting_writer?(@Counter.value) - end - - private - - # @!visibility private - def running_readers(c = @Counter.value) - c & MAX_READERS - end - - # @!visibility private - def running_readers?(c = @Counter.value) - (c & MAX_READERS) > 0 - end - - # @!visibility private - def running_writer?(c = @Counter.value) - c >= RUNNING_WRITER - end - - # @!visibility private - def waiting_writers(c = @Counter.value) - (c & MAX_WRITERS) / WAITING_WRITER - end - - # @!visibility private - def waiting_writer?(c = @Counter.value) - c >= WAITING_WRITER - end - - # @!visibility private - def max_readers?(c = @Counter.value) - (c & MAX_READERS) == MAX_READERS - end - - # @!visibility private - def max_writers?(c = @Counter.value) - (c & MAX_WRITERS) == MAX_WRITERS - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/reentrant_read_write_lock.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,379 +0,0 @@ -require 'thread' -require 'concurrent/atomic/atomic_reference' -require 'concurrent/errors' -require 'concurrent/synchronization' -require 'concurrent/atomic/thread_local_var' - -module Concurrent - - # Re-entrant read-write lock implementation - # - # Allows any number of concurrent readers, but only one concurrent writer - # (And while the "write" lock is taken, no read locks can be obtained either. - # Hence, the write lock can also be called an "exclusive" lock.) - # - # If another thread has taken a read lock, any thread which wants a write lock - # will block until all the readers release their locks. However, once a thread - # starts waiting to obtain a write lock, any additional readers that come along - # will also wait (so writers are not starved). - # - # A thread can acquire both a read and write lock at the same time. A thread can - # also acquire a read lock OR a write lock more than once. Only when the read (or - # write) lock is released as many times as it was acquired, will the thread - # actually let it go, allowing other threads which might have been waiting - # to proceed. Therefore the lock can be upgraded by first acquiring - # read lock and then write lock and that the lock can be downgraded by first - # having both read and write lock a releasing just the write lock. - # - # If both read and write locks are acquired by the same thread, it is not strictly - # necessary to release them in the same order they were acquired. In other words, - # the following code is legal: - # - # @example - # lock = Concurrent::ReentrantReadWriteLock.new - # lock.acquire_write_lock - # lock.acquire_read_lock - # lock.release_write_lock - # # At this point, the current thread is holding only a read lock, not a write - # # lock. So other threads can take read locks, but not a write lock. - # lock.release_read_lock - # # Now the current thread is not holding either a read or write lock, so - # # another thread could potentially acquire a write lock. - # - # This implementation was inspired by `java.util.concurrent.ReentrantReadWriteLock`. - # - # @example - # lock = Concurrent::ReentrantReadWriteLock.new - # lock.with_read_lock { data.retrieve } - # lock.with_write_lock { data.modify! } - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock - class ReentrantReadWriteLock < Synchronization::Object - - # Implementation notes: - # - # A goal is to make the uncontended path for both readers/writers mutex-free - # Only if there is reader-writer or writer-writer contention, should mutexes be used - # Otherwise, a single CAS operation is all we need to acquire/release a lock - # - # Internal state is represented by a single integer ("counter"), and updated - # using atomic compare-and-swap operations - # When the counter is 0, the lock is free - # Each thread which has one OR MORE read locks increments the counter by 1 - # (and decrements by 1 when releasing the read lock) - # The counter is increased by (1 << 15) for each writer waiting to acquire the - # write lock, and by (1 << 29) if the write lock is taken - # - # Additionally, each thread uses a thread-local variable to count how many times - # it has acquired a read lock, AND how many times it has acquired a write lock. - # It uses a similar trick; an increment of 1 means a read lock was taken, and - # an increment of (1 << 15) means a write lock was taken - # This is what makes re-entrancy possible - # - # 2 rules are followed to ensure good liveness properties: - # 1) Once a writer has queued up and is waiting for a write lock, no other thread - # can take a lock without waiting - # 2) When a write lock is released, readers are given the "first chance" to wake - # up and acquire a read lock - # Following these rules means readers and writers tend to "take turns", so neither - # can starve the other, even under heavy contention - - # @!visibility private - READER_BITS = 15 - # @!visibility private - WRITER_BITS = 14 - - # Used with @Counter: - # @!visibility private - WAITING_WRITER = 1 << READER_BITS - # @!visibility private - RUNNING_WRITER = 1 << (READER_BITS + WRITER_BITS) - # @!visibility private - MAX_READERS = WAITING_WRITER - 1 - # @!visibility private - MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 - - # Used with @HeldCount: - # @!visibility private - WRITE_LOCK_HELD = 1 << READER_BITS - # @!visibility private - READ_LOCK_MASK = WRITE_LOCK_HELD - 1 - # @!visibility private - WRITE_LOCK_MASK = MAX_WRITERS - - safe_initialization! - - # Create a new `ReentrantReadWriteLock` in the unlocked state. - def initialize - super() - @Counter = AtomicFixnum.new(0) # single integer which represents lock state - @ReadQueue = Synchronization::Lock.new # used to queue waiting readers - @WriteQueue = Synchronization::Lock.new # used to queue waiting writers - @HeldCount = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread - end - - # Execute a block operation within a read lock. - # - # @yield the task to be performed within the lock. - # - # @return [Object] the result of the block operation. - # - # @raise [ArgumentError] when no block is given. - # @raise [Concurrent::ResourceLimitError] if the maximum number of readers - # is exceeded. - def with_read_lock - raise ArgumentError.new('no block given') unless block_given? - acquire_read_lock - begin - yield - ensure - release_read_lock - end - end - - # Execute a block operation within a write lock. - # - # @yield the task to be performed within the lock. - # - # @return [Object] the result of the block operation. - # - # @raise [ArgumentError] when no block is given. - # @raise [Concurrent::ResourceLimitError] if the maximum number of readers - # is exceeded. - def with_write_lock - raise ArgumentError.new('no block given') unless block_given? - acquire_write_lock - begin - yield - ensure - release_write_lock - end - end - - # Acquire a read lock. If a write lock is held by another thread, will block - # until it is released. - # - # @return [Boolean] true if the lock is successfully acquired - # - # @raise [Concurrent::ResourceLimitError] if the maximum number of readers - # is exceeded. - def acquire_read_lock - if (held = @HeldCount.value) > 0 - # If we already have a lock, there's no need to wait - if held & READ_LOCK_MASK == 0 - # But we do need to update the counter, if we were holding a write - # lock but not a read lock - @Counter.update { |c| c + 1 } - end - @HeldCount.value = held + 1 - return true - end - - while true - c = @Counter.value - raise ResourceLimitError.new('Too many reader threads') if max_readers?(c) - - # If a writer is waiting OR running when we first queue up, we need to wait - if waiting_or_running_writer?(c) - # Before going to sleep, check again with the ReadQueue mutex held - @ReadQueue.synchronize do - @ReadQueue.ns_wait if waiting_or_running_writer? - end - # Note: the above 'synchronize' block could have used #wait_until, - # but that waits repeatedly in a loop, checking the wait condition - # each time it wakes up (to protect against spurious wakeups) - # But we are already in a loop, which is only broken when we successfully - # acquire the lock! So we don't care about spurious wakeups, and would - # rather not pay the extra overhead of using #wait_until - - # After a reader has waited once, they are allowed to "barge" ahead of waiting writers - # But if a writer is *running*, the reader still needs to wait (naturally) - while true - c = @Counter.value - if running_writer?(c) - @ReadQueue.synchronize do - @ReadQueue.ns_wait if running_writer? - end - elsif @Counter.compare_and_set(c, c+1) - @HeldCount.value = held + 1 - return true - end - end - elsif @Counter.compare_and_set(c, c+1) - @HeldCount.value = held + 1 - return true - end - end - end - - # Try to acquire a read lock and return true if we succeed. If it cannot be - # acquired immediately, return false. - # - # @return [Boolean] true if the lock is successfully acquired - def try_read_lock - if (held = @HeldCount.value) > 0 - if held & READ_LOCK_MASK == 0 - # If we hold a write lock, but not a read lock... - @Counter.update { |c| c + 1 } - end - @HeldCount.value = held + 1 - return true - else - c = @Counter.value - if !waiting_or_running_writer?(c) && @Counter.compare_and_set(c, c+1) - @HeldCount.value = held + 1 - return true - end - end - false - end - - # Release a previously acquired read lock. - # - # @return [Boolean] true if the lock is successfully released - def release_read_lock - held = @HeldCount.value = @HeldCount.value - 1 - rlocks_held = held & READ_LOCK_MASK - if rlocks_held == 0 - c = @Counter.update { |counter| counter - 1 } - # If one or more writers were waiting, and we were the last reader, wake a writer up - if waiting_or_running_writer?(c) && running_readers(c) == 0 - @WriteQueue.signal - end - elsif rlocks_held == READ_LOCK_MASK - raise IllegalOperationError, "Cannot release a read lock which is not held" - end - true - end - - # Acquire a write lock. Will block and wait for all active readers and writers. - # - # @return [Boolean] true if the lock is successfully acquired - # - # @raise [Concurrent::ResourceLimitError] if the maximum number of writers - # is exceeded. - def acquire_write_lock - if (held = @HeldCount.value) >= WRITE_LOCK_HELD - # if we already have a write (exclusive) lock, there's no need to wait - @HeldCount.value = held + WRITE_LOCK_HELD - return true - end - - while true - c = @Counter.value - raise ResourceLimitError.new('Too many writer threads') if max_writers?(c) - - # To go ahead and take the lock without waiting, there must be no writer - # running right now, AND no writers who came before us still waiting to - # acquire the lock - # Additionally, if any read locks have been taken, we must hold all of them - if c == held - # If we successfully swap the RUNNING_WRITER bit on, then we can go ahead - if @Counter.compare_and_set(c, c+RUNNING_WRITER) - @HeldCount.value = held + WRITE_LOCK_HELD - return true - end - elsif @Counter.compare_and_set(c, c+WAITING_WRITER) - while true - # Now we have successfully incremented, so no more readers will be able to increment - # (they will wait instead) - # However, readers OR writers could decrement right here - @WriteQueue.synchronize do - # So we have to do another check inside the synchronized section - # If a writer OR another reader is running, then go to sleep - c = @Counter.value - @WriteQueue.ns_wait if running_writer?(c) || running_readers(c) != held - end - # Note: if you are thinking of replacing the above 'synchronize' block - # with #wait_until, read the comment in #acquire_read_lock first! - - # We just came out of a wait - # If we successfully turn the RUNNING_WRITER bit on with an atomic swap, - # then we are OK to stop waiting and go ahead - # Otherwise go back and wait again - c = @Counter.value - if !running_writer?(c) && - running_readers(c) == held && - @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER) - @HeldCount.value = held + WRITE_LOCK_HELD - return true - end - end - end - end - end - - # Try to acquire a write lock and return true if we succeed. If it cannot be - # acquired immediately, return false. - # - # @return [Boolean] true if the lock is successfully acquired - def try_write_lock - if (held = @HeldCount.value) >= WRITE_LOCK_HELD - @HeldCount.value = held + WRITE_LOCK_HELD - return true - else - c = @Counter.value - if !waiting_or_running_writer?(c) && - running_readers(c) == held && - @Counter.compare_and_set(c, c+RUNNING_WRITER) - @HeldCount.value = held + WRITE_LOCK_HELD - return true - end - end - false - end - - # Release a previously acquired write lock. - # - # @return [Boolean] true if the lock is successfully released - def release_write_lock - held = @HeldCount.value = @HeldCount.value - WRITE_LOCK_HELD - wlocks_held = held & WRITE_LOCK_MASK - if wlocks_held == 0 - c = @Counter.update { |counter| counter - RUNNING_WRITER } - @ReadQueue.broadcast - @WriteQueue.signal if waiting_writers(c) > 0 - elsif wlocks_held == WRITE_LOCK_MASK - raise IllegalOperationError, "Cannot release a write lock which is not held" - end - true - end - - private - - # @!visibility private - def running_readers(c = @Counter.value) - c & MAX_READERS - end - - # @!visibility private - def running_readers?(c = @Counter.value) - (c & MAX_READERS) > 0 - end - - # @!visibility private - def running_writer?(c = @Counter.value) - c >= RUNNING_WRITER - end - - # @!visibility private - def waiting_writers(c = @Counter.value) - (c & MAX_WRITERS) >> READER_BITS - end - - # @!visibility private - def waiting_or_running_writer?(c = @Counter.value) - c >= WAITING_WRITER - end - - # @!visibility private - def max_readers?(c = @Counter.value) - (c & MAX_READERS) == MAX_READERS - end - - # @!visibility private - def max_writers?(c = @Counter.value) - (c & MAX_WRITERS) == MAX_WRITERS - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/ruby_thread_local_var.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,161 +0,0 @@ -require 'thread' -require 'concurrent/atomic/abstract_thread_local_var' - -module Concurrent - - # @!visibility private - # @!macro internal_implementation_note - class RubyThreadLocalVar < AbstractThreadLocalVar - - # Each thread has a (lazily initialized) array of thread-local variable values - # Each time a new thread-local var is created, we allocate an "index" for it - # For example, if the allocated index is 1, that means slot #1 in EVERY - # thread's thread-local array will be used for the value of that TLV - # - # The good thing about using a per-THREAD structure to hold values, rather - # than a per-TLV structure, is that no synchronization is needed when - # reading and writing those values (since the structure is only ever - # accessed by a single thread) - # - # Of course, when a TLV is GC'd, 1) we need to recover its index for use - # by other new TLVs (otherwise the thread-local arrays could get bigger - # and bigger with time), and 2) we need to null out all the references - # held in the now-unused slots (both to avoid blocking GC of those objects, - # and also to prevent "stale" values from being passed on to a new TLV - # when the index is reused) - # Because we need to null out freed slots, we need to keep references to - # ALL the thread-local arrays -- ARRAYS is for that - # But when a Thread is GC'd, we need to drop the reference to its thread-local - # array, so we don't leak memory - - # @!visibility private - FREE = [] - LOCK = Mutex.new - ARRAYS = {} # used as a hash set - @@next = 0 - private_constant :FREE, :LOCK, :ARRAYS - - # @!macro thread_local_var_method_get - def value - if array = get_threadlocal_array - value = array[@index] - if value.nil? - default - elsif value.equal?(NULL) - nil - else - value - end - else - default - end - end - - # @!macro thread_local_var_method_set - def value=(value) - me = Thread.current - # We could keep the thread-local arrays in a hash, keyed by Thread - # But why? That would require locking - # Using Ruby's built-in thread-local storage is faster - unless array = get_threadlocal_array(me) - array = set_threadlocal_array([], me) - LOCK.synchronize { ARRAYS[array.object_id] = array } - ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array)) - end - array[@index] = (value.nil? ? NULL : value) - value - end - - protected - - # @!visibility private - def allocate_storage - @index = LOCK.synchronize do - FREE.pop || begin - result = @@next - @@next += 1 - result - end - end - ObjectSpace.define_finalizer(self, self.class.threadlocal_finalizer(@index)) - end - - # @!visibility private - def self.threadlocal_finalizer(index) - proc do - Thread.new do # avoid error: can't be called from trap context - LOCK.synchronize do - FREE.push(index) - # The cost of GC'ing a TLV is linear in the number of threads using TLVs - # But that is natural! More threads means more storage is used per TLV - # So naturally more CPU time is required to free more storage - ARRAYS.each_value do |array| - array[index] = nil - end - end - end - end - end - - # @!visibility private - def self.thread_finalizer(array) - proc do - Thread.new do # avoid error: can't be called from trap context - LOCK.synchronize do - # The thread which used this thread-local array is now gone - # So don't hold onto a reference to the array (thus blocking GC) - ARRAYS.delete(array.object_id) - end - end - end - end - - private - - if Thread.instance_methods.include?(:thread_variable_get) - - def get_threadlocal_array(thread = Thread.current) - thread.thread_variable_get(:__threadlocal_array__) - end - - def set_threadlocal_array(array, thread = Thread.current) - thread.thread_variable_set(:__threadlocal_array__, array) - end - - else - - def get_threadlocal_array(thread = Thread.current) - thread[:__threadlocal_array__] - end - - def set_threadlocal_array(array, thread = Thread.current) - thread[:__threadlocal_array__] = array - end - end - - # This exists only for use in testing - # @!visibility private - def value_for(thread) - if array = get_threadlocal_array(thread) - value = array[@index] - if value.nil? - default_for(thread) - elsif value.equal?(NULL) - nil - else - value - end - else - default_for(thread) - end - end - - def default_for(thread) - if @default_block - raise "Cannot use default_for with default block" - else - @default - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/semaphore.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,145 +0,0 @@ -require 'concurrent/atomic/mutex_semaphore' -require 'concurrent/synchronization' - -module Concurrent - - ################################################################### - - # @!macro semaphore_method_initialize - # - # Create a new `Semaphore` with the initial `count`. - # - # @param [Fixnum] count the initial count - # - # @raise [ArgumentError] if `count` is not an integer or is less than zero - - # @!macro semaphore_method_acquire - # - # Acquires the given number of permits from this semaphore, - # blocking until all are available. - # - # @param [Fixnum] permits Number of permits to acquire - # - # @raise [ArgumentError] if `permits` is not an integer or is less than - # one - # - # @return [nil] - - # @!macro semaphore_method_available_permits - # - # Returns the current number of permits available in this semaphore. - # - # @return [Integer] - - # @!macro semaphore_method_drain_permits - # - # Acquires and returns all permits that are immediately available. - # - # @return [Integer] - - # @!macro semaphore_method_try_acquire - # - # Acquires the given number of permits from this semaphore, - # only if all are available at the time of invocation or within - # `timeout` interval - # - # @param [Fixnum] permits the number of permits to acquire - # - # @param [Fixnum] timeout the number of seconds to wait for the counter - # or `nil` to return immediately - # - # @raise [ArgumentError] if `permits` is not an integer or is less than - # one - # - # @return [Boolean] `false` if no permits are available, `true` when - # acquired a permit - - # @!macro semaphore_method_release - # - # Releases the given number of permits, returning them to the semaphore. - # - # @param [Fixnum] permits Number of permits to return to the semaphore. - # - # @raise [ArgumentError] if `permits` is not a number or is less than one - # - # @return [nil] - - ################################################################### - - # @!macro semaphore_public_api - # - # @!method initialize(count) - # @!macro semaphore_method_initialize - # - # @!method acquire(permits = 1) - # @!macro semaphore_method_acquire - # - # @!method available_permits - # @!macro semaphore_method_available_permits - # - # @!method drain_permits - # @!macro semaphore_method_drain_permits - # - # @!method try_acquire(permits = 1, timeout = nil) - # @!macro semaphore_method_try_acquire - # - # @!method release(permits = 1) - # @!macro semaphore_method_release - - ################################################################### - - # @!visibility private - # @!macro internal_implementation_note - SemaphoreImplementation = case - when defined?(JavaSemaphore) - JavaSemaphore - else - MutexSemaphore - end - private_constant :SemaphoreImplementation - - # @!macro semaphore - # - # A counting semaphore. Conceptually, a semaphore maintains a set of - # permits. Each {#acquire} blocks if necessary until a permit is - # available, and then takes it. Each {#release} adds a permit, potentially - # releasing a blocking acquirer. - # However, no actual permit objects are used; the Semaphore just keeps a - # count of the number available and acts accordingly. - # - # @!macro semaphore_public_api - # @example - # semaphore = Concurrent::Semaphore.new(2) - # - # t1 = Thread.new do - # semaphore.acquire - # puts "Thread 1 acquired semaphore" - # end - # - # t2 = Thread.new do - # semaphore.acquire - # puts "Thread 2 acquired semaphore" - # end - # - # t3 = Thread.new do - # semaphore.acquire - # puts "Thread 3 acquired semaphore" - # end - # - # t4 = Thread.new do - # sleep(2) - # puts "Thread 4 releasing semaphore" - # semaphore.release - # end - # - # [t1, t2, t3, t4].each(&:join) - # - # # prints: - # # Thread 3 acquired semaphore - # # Thread 2 acquired semaphore - # # Thread 4 releasing semaphore - # # Thread 1 acquired semaphore - # - class Semaphore < SemaphoreImplementation - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic/thread_local_var.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -require 'concurrent/atomic/ruby_thread_local_var' -require 'concurrent/atomic/java_thread_local_var' -require 'concurrent/utility/engine' - -module Concurrent - - ################################################################### - - # @!macro thread_local_var_method_initialize - # - # Creates a thread local variable. - # - # @param [Object] default the default value when otherwise unset - # @param [Proc] default_block Optional block that gets called to obtain the - # default value for each thread - - # @!macro thread_local_var_method_get - # - # Returns the value in the current thread's copy of this thread-local variable. - # - # @return [Object] the current value - - # @!macro thread_local_var_method_set - # - # Sets the current thread's copy of this thread-local variable to the specified value. - # - # @param [Object] value the value to set - # @return [Object] the new value - - # @!macro thread_local_var_method_bind - # - # Bind the given value to thread local storage during - # execution of the given block. - # - # @param [Object] value the value to bind - # @yield the operation to be performed with the bound variable - # @return [Object] the value - - - ################################################################### - - # @!macro thread_local_var_public_api - # - # @!method initialize(default = nil, &default_block) - # @!macro thread_local_var_method_initialize - # - # @!method value - # @!macro thread_local_var_method_get - # - # @!method value=(value) - # @!macro thread_local_var_method_set - # - # @!method bind(value, &block) - # @!macro thread_local_var_method_bind - - ################################################################### - - # @!visibility private - # @!macro internal_implementation_note - ThreadLocalVarImplementation = case - when Concurrent.on_jruby? - JavaThreadLocalVar - else - RubyThreadLocalVar - end - private_constant :ThreadLocalVarImplementation - - # @!macro thread_local_var - # - # A `ThreadLocalVar` is a variable where the value is different for each thread. - # Each variable may have a default value, but when you modify the variable only - # the current thread will ever see that change. - # - # @!macro thread_safe_variable_comparison - # - # @example - # v = ThreadLocalVar.new(14) - # v.value #=> 14 - # v.value = 2 - # v.value #=> 2 - # - # @example - # v = ThreadLocalVar.new(14) - # - # t1 = Thread.new do - # v.value #=> 14 - # v.value = 1 - # v.value #=> 1 - # end - # - # t2 = Thread.new do - # v.value #=> 14 - # v.value = 2 - # v.value #=> 2 - # end - # - # v.value #=> 14 - # - # @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal - # - # @!macro thread_local_var_public_api - class ThreadLocalVar < ThreadLocalVarImplementation - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/mutex_atomic.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -module Concurrent - - # @!visibility private - # @!macro internal_implementation_note - class MutexAtomicReference < Synchronization::LockableObject - include AtomicDirectUpdate - include AtomicNumericCompareAndSetWrapper - alias_method :compare_and_swap, :compare_and_set - - # @!macro atomic_reference_method_initialize - def initialize(value = nil) - super() - synchronize { ns_initialize(value) } - end - - # @!macro atomic_reference_method_get - def get - synchronize { @value } - end - alias_method :value, :get - - # @!macro atomic_reference_method_set - def set(new_value) - synchronize { @value = new_value } - end - alias_method :value=, :set - - # @!macro atomic_reference_method_get_and_set - def get_and_set(new_value) - synchronize do - old_value = @value - @value = new_value - old_value - end - end - alias_method :swap, :get_and_set - - # @!macro atomic_reference_method_compare_and_set - def _compare_and_set(old_value, new_value) - synchronize do - if @value.equal? old_value - @value = new_value - true - else - false - end - end - end - - protected - - def ns_initialize(value) - @value = value - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -module Concurrent - - # Special "compare and set" handling of numeric values. - # - # @!visibility private - # @!macro internal_implementation_note - module AtomicNumericCompareAndSetWrapper - - # @!macro atomic_reference_method_compare_and_set - def compare_and_set(old_value, new_value) - if old_value.kind_of? Numeric - while true - old = get - - return false unless old.kind_of? Numeric - - return false unless old == old_value - - result = _compare_and_set(old, new_value) - return result if result - end - else - _compare_and_set(old_value, new_value) - end - end - - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/atomics.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -require 'concurrent/atomic/atomic_reference' -require 'concurrent/atomic/atomic_boolean' -require 'concurrent/atomic/atomic_fixnum' -require 'concurrent/atomic/cyclic_barrier' -require 'concurrent/atomic/count_down_latch' -require 'concurrent/atomic/event' -require 'concurrent/atomic/read_write_lock' -require 'concurrent/atomic/reentrant_read_write_lock' -require 'concurrent/atomic/semaphore' -require 'concurrent/atomic/thread_local_var' diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_notify_observer_set.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -require 'concurrent/synchronization' - -module Concurrent - module Collection - - # A thread safe observer set implemented using copy-on-read approach: - # observers are added and removed from a thread safe collection; every time - # a notification is required the internal data structure is copied to - # prevent concurrency issues - # - # @api private - class CopyOnNotifyObserverSet < Synchronization::LockableObject - - def initialize - super() - synchronize { ns_initialize } - end - - # @!macro observable_add_observer - def add_observer(observer = nil, func = :update, &block) - if observer.nil? && block.nil? - raise ArgumentError, 'should pass observer as a first argument or block' - elsif observer && block - raise ArgumentError.new('cannot provide both an observer and a block') - end - - if block - observer = block - func = :call - end - - synchronize do - @observers[observer] = func - observer - end - end - - # @!macro observable_delete_observer - def delete_observer(observer) - synchronize do - @observers.delete(observer) - observer - end - end - - # @!macro observable_delete_observers - def delete_observers - synchronize do - @observers.clear - self - end - end - - # @!macro observable_count_observers - def count_observers - synchronize { @observers.count } - end - - # Notifies all registered observers with optional args - # @param [Object] args arguments to be passed to each observer - # @return [CopyOnWriteObserverSet] self - def notify_observers(*args, &block) - observers = duplicate_observers - notify_to(observers, *args, &block) - self - end - - # Notifies all registered observers with optional args and deletes them. - # - # @param [Object] args arguments to be passed to each observer - # @return [CopyOnWriteObserverSet] self - def notify_and_delete_observers(*args, &block) - observers = duplicate_and_clear_observers - notify_to(observers, *args, &block) - self - end - - protected - - def ns_initialize - @observers = {} - end - - private - - def duplicate_and_clear_observers - synchronize do - observers = @observers.dup - @observers.clear - observers - end - end - - def duplicate_observers - synchronize { @observers.dup } - end - - def notify_to(observers, *args) - raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty? - observers.each do |observer, function| - args = yield if block_given? - observer.send(function, *args) - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/copy_on_write_observer_set.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -require 'concurrent/synchronization' - -module Concurrent - module Collection - - # A thread safe observer set implemented using copy-on-write approach: - # every time an observer is added or removed the whole internal data structure is - # duplicated and replaced with a new one. - # - # @api private - class CopyOnWriteObserverSet < Synchronization::LockableObject - - def initialize - super() - synchronize { ns_initialize } - end - - # @!macro observable_add_observer - def add_observer(observer = nil, func = :update, &block) - if observer.nil? && block.nil? - raise ArgumentError, 'should pass observer as a first argument or block' - elsif observer && block - raise ArgumentError.new('cannot provide both an observer and a block') - end - - if block - observer = block - func = :call - end - - synchronize do - new_observers = @observers.dup - new_observers[observer] = func - @observers = new_observers - observer - end - end - - # @!macro observable_delete_observer - def delete_observer(observer) - synchronize do - new_observers = @observers.dup - new_observers.delete(observer) - @observers = new_observers - observer - end - end - - # @!macro observable_delete_observers - def delete_observers - self.observers = {} - self - end - - # @!macro observable_count_observers - def count_observers - observers.count - end - - # Notifies all registered observers with optional args - # @param [Object] args arguments to be passed to each observer - # @return [CopyOnWriteObserverSet] self - def notify_observers(*args, &block) - notify_to(observers, *args, &block) - self - end - - # Notifies all registered observers with optional args and deletes them. - # - # @param [Object] args arguments to be passed to each observer - # @return [CopyOnWriteObserverSet] self - def notify_and_delete_observers(*args, &block) - old = clear_observers_and_return_old - notify_to(old, *args, &block) - self - end - - protected - - def ns_initialize - @observers = {} - end - - private - - def notify_to(observers, *args) - raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty? - observers.each do |observer, function| - args = yield if block_given? - observer.send(function, *args) - end - end - - def observers - synchronize { @observers } - end - - def observers=(new_set) - synchronize { @observers = new_set } - end - - def clear_observers_and_return_old - synchronize do - old_observers = @observers - @observers = {} - old_observers - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/java_non_concurrent_priority_queue.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -if Concurrent.on_jruby? - - module Concurrent - module Collection - - - # @!macro priority_queue - # - # @!visibility private - # @!macro internal_implementation_note - class JavaNonConcurrentPriorityQueue - - # @!macro priority_queue_method_initialize - def initialize(opts = {}) - order = opts.fetch(:order, :max) - if [:min, :low].include?(order) - @queue = java.util.PriorityQueue.new(11) # 11 is the default initial capacity - else - @queue = java.util.PriorityQueue.new(11, java.util.Collections.reverseOrder()) - end - end - - # @!macro priority_queue_method_clear - def clear - @queue.clear - true - end - - # @!macro priority_queue_method_delete - def delete(item) - found = false - while @queue.remove(item) do - found = true - end - found - end - - # @!macro priority_queue_method_empty - def empty? - @queue.size == 0 - end - - # @!macro priority_queue_method_include - def include?(item) - @queue.contains(item) - end - alias_method :has_priority?, :include? - - # @!macro priority_queue_method_length - def length - @queue.size - end - alias_method :size, :length - - # @!macro priority_queue_method_peek - def peek - @queue.peek - end - - # @!macro priority_queue_method_pop - def pop - @queue.poll - end - alias_method :deq, :pop - alias_method :shift, :pop - - # @!macro priority_queue_method_push - def push(item) - raise ArgumentError.new('cannot enqueue nil') if item.nil? - @queue.add(item) - end - alias_method :<<, :push - alias_method :enq, :push - - # @!macro priority_queue_method_from_list - def self.from_list(list, opts = {}) - queue = new(opts) - list.each{|item| queue << item } - queue - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/lock_free_stack.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -module Concurrent - - # @!macro warn.edge - class LockFreeStack < Synchronization::Object - - safe_initialization! - - class Node - # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class? - - # @return [Node] - attr_reader :next_node - - # @return [Object] - attr_reader :value - - # @!visibility private - # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised - attr_writer :value - - def initialize(value, next_node) - @value = value - @next_node = next_node - end - - singleton_class.send :alias_method, :[], :new - end - - # The singleton for empty node - EMPTY = Node[nil, nil] - def EMPTY.next_node - self - end - - attr_atomic(:head) - private :head, :head=, :swap_head, :compare_and_set_head, :update_head - - # @!visibility private - def self.of1(value) - new Node[value, EMPTY] - end - - # @!visibility private - def self.of2(value1, value2) - new Node[value1, Node[value2, EMPTY]] - end - - # @param [Node] head - def initialize(head = EMPTY) - super() - self.head = head - end - - # @param [Node] head - # @return [true, false] - def empty?(head = head()) - head.equal? EMPTY - end - - # @param [Node] head - # @param [Object] value - # @return [true, false] - def compare_and_push(head, value) - compare_and_set_head head, Node[value, head] - end - - # @param [Object] value - # @return [self] - def push(value) - while true - current_head = head - return self if compare_and_set_head current_head, Node[value, current_head] - end - end - - # @return [Node] - def peek - head - end - - # @param [Node] head - # @return [true, false] - def compare_and_pop(head) - compare_and_set_head head, head.next_node - end - - # @return [Object] - def pop - while true - current_head = head - return current_head.value if compare_and_set_head current_head, current_head.next_node - end - end - - # @param [Node] head - # @return [true, false] - def compare_and_clear(head) - compare_and_set_head head, EMPTY - end - - include Enumerable - - # @param [Node] head - # @return [self] - def each(head = nil) - return to_enum(:each, head) unless block_given? - it = head || peek - until it.equal?(EMPTY) - yield it.value - it = it.next_node - end - self - end - - # @return [true, false] - def clear - while true - current_head = head - return false if current_head == EMPTY - return true if compare_and_set_head current_head, EMPTY - end - end - - # @param [Node] head - # @return [true, false] - def clear_if(head) - compare_and_set_head head, EMPTY - end - - # @param [Node] head - # @param [Node] new_head - # @return [true, false] - def replace_if(head, new_head) - compare_and_set_head head, new_head - end - - # @return [self] - # @yield over the cleared stack - # @yieldparam [Object] value - def clear_each(&block) - while true - current_head = head - return self if current_head == EMPTY - if compare_and_set_head current_head, EMPTY - each current_head, &block - return self - end - end - end - - # @return [String] Short string representation. - def to_s - format '%s %s>', super[0..-2], to_a.to_s - end - - alias_method :inspect, :to_s - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/atomic_reference_map_backend.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,927 +0,0 @@ -require 'concurrent/constants' -require 'concurrent/thread_safe/util' -require 'concurrent/thread_safe/util/adder' -require 'concurrent/thread_safe/util/cheap_lockable' -require 'concurrent/thread_safe/util/power_of_two_tuple' -require 'concurrent/thread_safe/util/volatile' -require 'concurrent/thread_safe/util/xor_shift_random' - -module Concurrent - - # @!visibility private - module Collection - - # A Ruby port of the Doug Lea's jsr166e.ConcurrentHashMapV8 class version 1.59 - # available in public domain. - # - # Original source code available here: - # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.59 - # - # The Ruby port skips out the +TreeBin+ (red-black trees for use in bins whose - # size exceeds a threshold). - # - # A hash table supporting full concurrency of retrievals and high expected - # concurrency for updates. However, even though all operations are - # thread-safe, retrieval operations do _not_ entail locking, and there is - # _not_ any support for locking the entire table in a way that prevents all - # access. - # - # Retrieval operations generally do not block, so may overlap with update - # operations. Retrievals reflect the results of the most recently _completed_ - # update operations holding upon their onset. (More formally, an update - # operation for a given key bears a _happens-before_ relation with any (non - # +nil+) retrieval for that key reporting the updated value.) For aggregate - # operations such as +clear()+, concurrent retrievals may reflect insertion or - # removal of only some entries. Similarly, the +each_pair+ iterator yields - # elements reflecting the state of the hash table at some point at or since - # the start of the +each_pair+. Bear in mind that the results of aggregate - # status methods including +size()+ and +empty?+} are typically useful only - # when a map is not undergoing concurrent updates in other threads. Otherwise - # the results of these methods reflect transient states that may be adequate - # for monitoring or estimation purposes, but not for program control. - # - # The table is dynamically expanded when there are too many collisions (i.e., - # keys that have distinct hash codes but fall into the same slot modulo the - # table size), with the expected average effect of maintaining roughly two - # bins per mapping (corresponding to a 0.75 load factor threshold for - # resizing). There may be much variance around this average as mappings are - # added and removed, but overall, this maintains a commonly accepted - # time/space tradeoff for hash tables. However, resizing this or any other - # kind of hash table may be a relatively slow operation. When possible, it is - # a good idea to provide a size estimate as an optional :initial_capacity - # initializer argument. An additional optional :load_factor constructor - # argument provides a further means of customizing initial table capacity by - # specifying the table density to be used in calculating the amount of space - # to allocate for the given number of elements. Note that using many keys with - # exactly the same +hash+ is a sure way to slow down performance of any hash - # table. - # - # ## Design overview - # - # The primary design goal of this hash table is to maintain concurrent - # readability (typically method +[]+, but also iteration and related methods) - # while minimizing update contention. Secondary goals are to keep space - # consumption about the same or better than plain +Hash+, and to support high - # initial insertion rates on an empty table by many threads. - # - # Each key-value mapping is held in a +Node+. The validation-based approach - # explained below leads to a lot of code sprawl because retry-control - # precludes factoring into smaller methods. - # - # The table is lazily initialized to a power-of-two size upon the first - # insertion. Each bin in the table normally contains a list of +Node+s (most - # often, the list has only zero or one +Node+). Table accesses require - # volatile/atomic reads, writes, and CASes. The lists of nodes within bins are - # always accurately traversable under volatile reads, so long as lookups check - # hash code and non-nullness of value before checking key equality. - # - # We use the top two bits of +Node+ hash fields for control purposes -- they - # are available anyway because of addressing constraints. As explained further - # below, these top bits are used as follows: - # - # - 00 - Normal - # - 01 - Locked - # - 11 - Locked and may have a thread waiting for lock - # - 10 - +Node+ is a forwarding node - # - # The lower 28 bits of each +Node+'s hash field contain a the key's hash code, - # except for forwarding nodes, for which the lower bits are zero (and so - # always have hash field == +MOVED+). - # - # Insertion (via +[]=+ or its variants) of the first node in an empty bin is - # performed by just CASing it to the bin. This is by far the most common case - # for put operations under most key/hash distributions. Other update - # operations (insert, delete, and replace) require locks. We do not want to - # waste the space required to associate a distinct lock object with each bin, - # so instead use the first node of a bin list itself as a lock. Blocking - # support for these locks relies +Concurrent::ThreadSafe::Util::CheapLockable. However, we also need a - # +try_lock+ construction, so we overlay these by using bits of the +Node+ - # hash field for lock control (see above), and so normally use builtin - # monitors only for blocking and signalling using - # +cheap_wait+/+cheap_broadcast+ constructions. See +Node#try_await_lock+. - # - # Using the first node of a list as a lock does not by itself suffice though: - # When a node is locked, any update must first validate that it is still the - # first node after locking it, and retry if not. Because new nodes are always - # appended to lists, once a node is first in a bin, it remains first until - # deleted or the bin becomes invalidated (upon resizing). However, operations - # that only conditionally update may inspect nodes until the point of update. - # This is a converse of sorts to the lazy locking technique described by - # Herlihy & Shavit. - # - # The main disadvantage of per-bin locks is that other update operations on - # other nodes in a bin list protected by the same lock can stall, for example - # when user +eql?+ or mapping functions take a long time. However, - # statistically, under random hash codes, this is not a common problem. - # Ideally, the frequency of nodes in bins follows a Poisson distribution - # (http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of - # about 0.5 on average, given the resizing threshold of 0.75, although with a - # large variance because of resizing granularity. Ignoring variance, the - # expected occurrences of list size k are (exp(-0.5) * pow(0.5, k) / - # factorial(k)). The first values are: - # - # - 0: 0.60653066 - # - 1: 0.30326533 - # - 2: 0.07581633 - # - 3: 0.01263606 - # - 4: 0.00157952 - # - 5: 0.00015795 - # - 6: 0.00001316 - # - 7: 0.00000094 - # - 8: 0.00000006 - # - more: less than 1 in ten million - # - # Lock contention probability for two threads accessing distinct elements is - # roughly 1 / (8 * #elements) under random hashes. - # - # The table is resized when occupancy exceeds a percentage threshold - # (nominally, 0.75, but see below). Only a single thread performs the resize - # (using field +size_control+, to arrange exclusion), but the table otherwise - # remains usable for reads and updates. Resizing proceeds by transferring - # bins, one by one, from the table to the next table. Because we are using - # power-of-two expansion, the elements from each bin must either stay at same - # index, or move with a power of two offset. We eliminate unnecessary node - # creation by catching cases where old nodes can be reused because their next - # fields won't change. On average, only about one-sixth of them need cloning - # when a table doubles. The nodes they replace will be garbage collectable as - # soon as they are no longer referenced by any reader thread that may be in - # the midst of concurrently traversing table. Upon transfer, the old table bin - # contains only a special forwarding node (with hash field +MOVED+) that - # contains the next table as its key. On encountering a forwarding node, - # access and update operations restart, using the new table. - # - # Each bin transfer requires its bin lock. However, unlike other cases, a - # transfer can skip a bin if it fails to acquire its lock, and revisit it - # later. Method +rebuild+ maintains a buffer of TRANSFER_BUFFER_SIZE bins that - # have been skipped because of failure to acquire a lock, and blocks only if - # none are available (i.e., only very rarely). The transfer operation must - # also ensure that all accessible bins in both the old and new table are - # usable by any traversal. When there are no lock acquisition failures, this - # is arranged simply by proceeding from the last bin (+table.size - 1+) up - # towards the first. Upon seeing a forwarding node, traversals arrange to move - # to the new table without revisiting nodes. However, when any node is skipped - # during a transfer, all earlier table bins may have become visible, so are - # initialized with a reverse-forwarding node back to the old table until the - # new ones are established. (This sometimes requires transiently locking a - # forwarding node, which is possible under the above encoding.) These more - # expensive mechanics trigger only when necessary. - # - # The traversal scheme also applies to partial traversals of - # ranges of bins (via an alternate Traverser constructor) - # to support partitioned aggregate operations. Also, read-only - # operations give up if ever forwarded to a null table, which - # provides support for shutdown-style clearing, which is also not - # currently implemented. - # - # Lazy table initialization minimizes footprint until first use. - # - # The element count is maintained using a +Concurrent::ThreadSafe::Util::Adder+, - # which avoids contention on updates but can encounter cache thrashing - # if read too frequently during concurrent access. To avoid reading so - # often, resizing is attempted either when a bin lock is - # contended, or upon adding to a bin already holding two or more - # nodes (checked before adding in the +x_if_absent+ methods, after - # adding in others). Under uniform hash distributions, the - # probability of this occurring at threshold is around 13%, - # meaning that only about 1 in 8 puts check threshold (and after - # resizing, many fewer do so). But this approximation has high - # variance for small table sizes, so we check on any collision - # for sizes <= 64. The bulk putAll operation further reduces - # contention by only committing count updates upon these size - # checks. - # - # @!visibility private - class AtomicReferenceMapBackend - - # @!visibility private - class Table < Concurrent::ThreadSafe::Util::PowerOfTwoTuple - def cas_new_node(i, hash, key, value) - cas(i, nil, Node.new(hash, key, value)) - end - - def try_to_cas_in_computed(i, hash, key) - succeeded = false - new_value = nil - new_node = Node.new(locked_hash = hash | LOCKED, key, NULL) - if cas(i, nil, new_node) - begin - if NULL == (new_value = yield(NULL)) - was_null = true - else - new_node.value = new_value - end - succeeded = true - ensure - volatile_set(i, nil) if !succeeded || was_null - new_node.unlock_via_hash(locked_hash, hash) - end - end - return succeeded, new_value - end - - def try_lock_via_hash(i, node, node_hash) - node.try_lock_via_hash(node_hash) do - yield if volatile_get(i) == node - end - end - - def delete_node_at(i, node, predecessor_node) - if predecessor_node - predecessor_node.next = node.next - else - volatile_set(i, node.next) - end - end - end - - # Key-value entry. Nodes with a hash field of +MOVED+ are special, and do - # not contain user keys or values. Otherwise, keys are never +nil+, and - # +NULL+ +value+ fields indicate that a node is in the process of being - # deleted or created. For purposes of read-only access, a key may be read - # before a value, but can only be used after checking value to be +!= NULL+. - # - # @!visibility private - class Node - extend Concurrent::ThreadSafe::Util::Volatile - attr_volatile :hash, :value, :next - - include Concurrent::ThreadSafe::Util::CheapLockable - - bit_shift = Concurrent::ThreadSafe::Util::FIXNUM_BIT_SIZE - 2 # need 2 bits for ourselves - # Encodings for special uses of Node hash fields. See above for explanation. - MOVED = ('10' << ('0' * bit_shift)).to_i(2) # hash field for forwarding nodes - LOCKED = ('01' << ('0' * bit_shift)).to_i(2) # set/tested only as a bit - WAITING = ('11' << ('0' * bit_shift)).to_i(2) # both bits set/tested together - HASH_BITS = ('00' << ('1' * bit_shift)).to_i(2) # usable bits of normal node hash - - SPIN_LOCK_ATTEMPTS = Concurrent::ThreadSafe::Util::CPU_COUNT > 1 ? Concurrent::ThreadSafe::Util::CPU_COUNT * 2 : 0 - - attr_reader :key - - def initialize(hash, key, value, next_node = nil) - super() - @key = key - self.lazy_set_hash(hash) - self.lazy_set_value(value) - self.next = next_node - end - - # Spins a while if +LOCKED+ bit set and this node is the first of its bin, - # and then sets +WAITING+ bits on hash field and blocks (once) if they are - # still set. It is OK for this method to return even if lock is not - # available upon exit, which enables these simple single-wait mechanics. - # - # The corresponding signalling operation is performed within callers: Upon - # detecting that +WAITING+ has been set when unlocking lock (via a failed - # CAS from non-waiting +LOCKED+ state), unlockers acquire the - # +cheap_synchronize+ lock and perform a +cheap_broadcast+. - def try_await_lock(table, i) - if table && i >= 0 && i < table.size # bounds check, TODO: why are we bounds checking? - spins = SPIN_LOCK_ATTEMPTS - randomizer = base_randomizer = Concurrent::ThreadSafe::Util::XorShiftRandom.get - while equal?(table.volatile_get(i)) && self.class.locked_hash?(my_hash = hash) - if spins >= 0 - if (randomizer = (randomizer >> 1)).even? # spin at random - if (spins -= 1) == 0 - Thread.pass # yield before blocking - else - randomizer = base_randomizer = Concurrent::ThreadSafe::Util::XorShiftRandom.xorshift(base_randomizer) if randomizer.zero? - end - end - elsif cas_hash(my_hash, my_hash | WAITING) - force_acquire_lock(table, i) - break - end - end - end - end - - def key?(key) - @key.eql?(key) - end - - def matches?(key, hash) - pure_hash == hash && key?(key) - end - - def pure_hash - hash & HASH_BITS - end - - def try_lock_via_hash(node_hash = hash) - if cas_hash(node_hash, locked_hash = node_hash | LOCKED) - begin - yield - ensure - unlock_via_hash(locked_hash, node_hash) - end - end - end - - def locked? - self.class.locked_hash?(hash) - end - - def unlock_via_hash(locked_hash, node_hash) - unless cas_hash(locked_hash, node_hash) - self.hash = node_hash - cheap_synchronize { cheap_broadcast } - end - end - - private - def force_acquire_lock(table, i) - cheap_synchronize do - if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING - cheap_wait - else - cheap_broadcast # possibly won race vs signaller - end - end - end - - class << self - def locked_hash?(hash) - (hash & LOCKED) != 0 - end - end - end - - # shorthands - MOVED = Node::MOVED - LOCKED = Node::LOCKED - WAITING = Node::WAITING - HASH_BITS = Node::HASH_BITS - - NOW_RESIZING = -1 - DEFAULT_CAPACITY = 16 - MAX_CAPACITY = Concurrent::ThreadSafe::Util::MAX_INT - - # The buffer size for skipped bins during transfers. The - # value is arbitrary but should be large enough to avoid - # most locking stalls during resizes. - TRANSFER_BUFFER_SIZE = 32 - - extend Concurrent::ThreadSafe::Util::Volatile - attr_volatile :table, # The array of bins. Lazily initialized upon first insertion. Size is always a power of two. - - # Table initialization and resizing control. When negative, the - # table is being initialized or resized. Otherwise, when table is - # null, holds the initial table size to use upon creation, or 0 - # for default. After initialization, holds the next element count - # value upon which to resize the table. - :size_control - - def initialize(options = nil) - super() - @counter = Concurrent::ThreadSafe::Util::Adder.new - initial_capacity = options && options[:initial_capacity] || DEFAULT_CAPACITY - self.size_control = (capacity = table_size_for(initial_capacity)) > MAX_CAPACITY ? MAX_CAPACITY : capacity - end - - def get_or_default(key, else_value = nil) - hash = key_hash(key) - current_table = table - while current_table - node = current_table.volatile_get_by_hash(hash) - current_table = - while node - if (node_hash = node.hash) == MOVED - break node.key - elsif (node_hash & HASH_BITS) == hash && node.key?(key) && NULL != (value = node.value) - return value - end - node = node.next - end - end - else_value - end - - def [](key) - get_or_default(key) - end - - def key?(key) - get_or_default(key, NULL) != NULL - end - - def []=(key, value) - get_and_set(key, value) - value - end - - def compute_if_absent(key) - hash = key_hash(key) - current_table = table || initialize_table - while true - if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) - succeeded, new_value = current_table.try_to_cas_in_computed(i, hash, key) { yield } - if succeeded - increment_size - return new_value - end - elsif (node_hash = node.hash) == MOVED - current_table = node.key - elsif NULL != (current_value = find_value_in_node_list(node, key, hash, node_hash & HASH_BITS)) - return current_value - elsif Node.locked_hash?(node_hash) - try_await_lock(current_table, i, node) - else - succeeded, value = attempt_internal_compute_if_absent(key, hash, current_table, i, node, node_hash) { yield } - return value if succeeded - end - end - end - - def compute_if_present(key) - new_value = nil - internal_replace(key) do |old_value| - if (new_value = yield(NULL == old_value ? nil : old_value)).nil? - NULL - else - new_value - end - end - new_value - end - - def compute(key) - internal_compute(key) do |old_value| - if (new_value = yield(NULL == old_value ? nil : old_value)).nil? - NULL - else - new_value - end - end - end - - def merge_pair(key, value) - internal_compute(key) do |old_value| - if NULL == old_value || !(value = yield(old_value)).nil? - value - else - NULL - end - end - end - - def replace_pair(key, old_value, new_value) - NULL != internal_replace(key, old_value) { new_value } - end - - def replace_if_exists(key, new_value) - if (result = internal_replace(key) { new_value }) && NULL != result - result - end - end - - def get_and_set(key, value) # internalPut in the original CHMV8 - hash = key_hash(key) - current_table = table || initialize_table - while true - if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) - if current_table.cas_new_node(i, hash, key, value) - increment_size - break - end - elsif (node_hash = node.hash) == MOVED - current_table = node.key - elsif Node.locked_hash?(node_hash) - try_await_lock(current_table, i, node) - else - succeeded, old_value = attempt_get_and_set(key, value, hash, current_table, i, node, node_hash) - break old_value if succeeded - end - end - end - - def delete(key) - replace_if_exists(key, NULL) - end - - def delete_pair(key, value) - result = internal_replace(key, value) { NULL } - if result && NULL != result - !!result - else - false - end - end - - def each_pair - return self unless current_table = table - current_table_size = base_size = current_table.size - i = base_index = 0 - while base_index < base_size - if node = current_table.volatile_get(i) - if node.hash == MOVED - current_table = node.key - current_table_size = current_table.size - else - begin - if NULL != (value = node.value) # skip deleted or special nodes - yield node.key, value - end - end while node = node.next - end - end - - if (i_with_base = i + base_size) < current_table_size - i = i_with_base # visit upper slots if present - else - i = base_index += 1 - end - end - self - end - - def size - (sum = @counter.sum) < 0 ? 0 : sum # ignore transient negative values - end - - def empty? - size == 0 - end - - # Implementation for clear. Steps through each bin, removing all nodes. - def clear - return self unless current_table = table - current_table_size = current_table.size - deleted_count = i = 0 - while i < current_table_size - if !(node = current_table.volatile_get(i)) - i += 1 - elsif (node_hash = node.hash) == MOVED - current_table = node.key - current_table_size = current_table.size - elsif Node.locked_hash?(node_hash) - decrement_size(deleted_count) # opportunistically update count - deleted_count = 0 - node.try_await_lock(current_table, i) - else - current_table.try_lock_via_hash(i, node, node_hash) do - begin - deleted_count += 1 if NULL != node.value # recheck under lock - node.value = nil - end while node = node.next - current_table.volatile_set(i, nil) - i += 1 - end - end - end - decrement_size(deleted_count) - self - end - - private - # Internal versions of the insertion methods, each a - # little more complicated than the last. All have - # the same basic structure: - # 1. If table uninitialized, create - # 2. If bin empty, try to CAS new node - # 3. If bin stale, use new table - # 4. Lock and validate; if valid, scan and add or update - # - # The others interweave other checks and/or alternative actions: - # * Plain +get_and_set+ checks for and performs resize after insertion. - # * compute_if_absent prescans for mapping without lock (and fails to add - # if present), which also makes pre-emptive resize checks worthwhile. - # - # Someday when details settle down a bit more, it might be worth - # some factoring to reduce sprawl. - def internal_replace(key, expected_old_value = NULL, &block) - hash = key_hash(key) - current_table = table - while current_table - if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) - break - elsif (node_hash = node.hash) == MOVED - current_table = node.key - elsif (node_hash & HASH_BITS) != hash && !node.next # precheck - break # rules out possible existence - elsif Node.locked_hash?(node_hash) - try_await_lock(current_table, i, node) - else - succeeded, old_value = attempt_internal_replace(key, expected_old_value, hash, current_table, i, node, node_hash, &block) - return old_value if succeeded - end - end - NULL - end - - def attempt_internal_replace(key, expected_old_value, hash, current_table, i, node, node_hash) - current_table.try_lock_via_hash(i, node, node_hash) do - predecessor_node = nil - old_value = NULL - begin - if node.matches?(key, hash) && NULL != (current_value = node.value) - if NULL == expected_old_value || expected_old_value == current_value # NULL == expected_old_value means whatever value - old_value = current_value - if NULL == (node.value = yield(old_value)) - current_table.delete_node_at(i, node, predecessor_node) - decrement_size - end - end - break - end - - predecessor_node = node - end while node = node.next - - return true, old_value - end - end - - def find_value_in_node_list(node, key, hash, pure_hash) - do_check_for_resize = false - while true - if pure_hash == hash && node.key?(key) && NULL != (value = node.value) - return value - elsif node = node.next - do_check_for_resize = true # at least 2 nodes -> check for resize - pure_hash = node.pure_hash - else - return NULL - end - end - ensure - check_for_resize if do_check_for_resize - end - - def internal_compute(key, &block) - hash = key_hash(key) - current_table = table || initialize_table - while true - if !(node = current_table.volatile_get(i = current_table.hash_to_index(hash))) - succeeded, new_value = current_table.try_to_cas_in_computed(i, hash, key, &block) - if succeeded - if NULL == new_value - break nil - else - increment_size - break new_value - end - end - elsif (node_hash = node.hash) == MOVED - current_table = node.key - elsif Node.locked_hash?(node_hash) - try_await_lock(current_table, i, node) - else - succeeded, new_value = attempt_compute(key, hash, current_table, i, node, node_hash, &block) - break new_value if succeeded - end - end - end - - def attempt_internal_compute_if_absent(key, hash, current_table, i, node, node_hash) - added = false - current_table.try_lock_via_hash(i, node, node_hash) do - while true - if node.matches?(key, hash) && NULL != (value = node.value) - return true, value - end - last = node - unless node = node.next - last.next = Node.new(hash, key, value = yield) - added = true - increment_size - return true, value - end - end - end - ensure - check_for_resize if added - end - - def attempt_compute(key, hash, current_table, i, node, node_hash) - added = false - current_table.try_lock_via_hash(i, node, node_hash) do - predecessor_node = nil - while true - if node.matches?(key, hash) && NULL != (value = node.value) - if NULL == (node.value = value = yield(value)) - current_table.delete_node_at(i, node, predecessor_node) - decrement_size - value = nil - end - return true, value - end - predecessor_node = node - unless node = node.next - if NULL == (value = yield(NULL)) - value = nil - else - predecessor_node.next = Node.new(hash, key, value) - added = true - increment_size - end - return true, value - end - end - end - ensure - check_for_resize if added - end - - def attempt_get_and_set(key, value, hash, current_table, i, node, node_hash) - node_nesting = nil - current_table.try_lock_via_hash(i, node, node_hash) do - node_nesting = 1 - old_value = nil - found_old_value = false - while node - if node.matches?(key, hash) && NULL != (old_value = node.value) - found_old_value = true - node.value = value - break - end - last = node - unless node = node.next - last.next = Node.new(hash, key, value) - break - end - node_nesting += 1 - end - - return true, old_value if found_old_value - increment_size - true - end - ensure - check_for_resize if node_nesting && (node_nesting > 1 || current_table.size <= 64) - end - - def initialize_copy(other) - super - @counter = Concurrent::ThreadSafe::Util::Adder.new - self.table = nil - self.size_control = (other_table = other.table) ? other_table.size : DEFAULT_CAPACITY - self - end - - def try_await_lock(current_table, i, node) - check_for_resize # try resizing if can't get lock - node.try_await_lock(current_table, i) - end - - def key_hash(key) - key.hash & HASH_BITS - end - - # Returns a power of two table size for the given desired capacity. - def table_size_for(entry_count) - size = 2 - size <<= 1 while size < entry_count - size - end - - # Initializes table, using the size recorded in +size_control+. - def initialize_table - until current_table ||= table - if (size_ctrl = size_control) == NOW_RESIZING - Thread.pass # lost initialization race; just spin - else - try_in_resize_lock(current_table, size_ctrl) do - initial_size = size_ctrl > 0 ? size_ctrl : DEFAULT_CAPACITY - current_table = self.table = Table.new(initial_size) - initial_size - (initial_size >> 2) # 75% load factor - end - end - end - current_table - end - - # If table is too small and not already resizing, creates next table and - # transfers bins. Rechecks occupancy after a transfer to see if another - # resize is already needed because resizings are lagging additions. - def check_for_resize - while (current_table = table) && MAX_CAPACITY > (table_size = current_table.size) && NOW_RESIZING != (size_ctrl = size_control) && size_ctrl < @counter.sum - try_in_resize_lock(current_table, size_ctrl) do - self.table = rebuild(current_table) - (table_size << 1) - (table_size >> 1) # 75% load factor - end - end - end - - def try_in_resize_lock(current_table, size_ctrl) - if cas_size_control(size_ctrl, NOW_RESIZING) - begin - if current_table == table # recheck under lock - size_ctrl = yield # get new size_control - end - ensure - self.size_control = size_ctrl - end - end - end - - # Moves and/or copies the nodes in each bin to new table. See above for explanation. - def rebuild(table) - old_table_size = table.size - new_table = table.next_in_size_table - # puts "#{old_table_size} -> #{new_table.size}" - forwarder = Node.new(MOVED, new_table, NULL) - rev_forwarder = nil - locked_indexes = nil # holds bins to revisit; nil until needed - locked_arr_idx = 0 - bin = old_table_size - 1 - i = bin - while true - if !(node = table.volatile_get(i)) - # no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table - redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder)) - elsif Node.locked_hash?(node_hash = node.hash) - locked_indexes ||= ::Array.new - if bin < 0 && locked_arr_idx > 0 - locked_arr_idx -= 1 - i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin - redo - end - if bin < 0 || locked_indexes.size >= TRANSFER_BUFFER_SIZE - node.try_await_lock(table, i) # no other options -- block - redo - end - rev_forwarder ||= Node.new(MOVED, table, NULL) - redo unless table.volatile_get(i) == node && node.locked? # recheck before adding to list - locked_indexes << i - new_table.volatile_set(i, rev_forwarder) - new_table.volatile_set(i + old_table_size, rev_forwarder) - else - redo unless split_old_bin(table, new_table, i, node, node_hash, forwarder) - end - - if bin > 0 - i = (bin -= 1) - elsif locked_indexes && !locked_indexes.empty? - bin = -1 - i = locked_indexes.pop - locked_arr_idx = locked_indexes.size - 1 - else - return new_table - end - end - end - - def lock_and_clean_up_reverse_forwarders(old_table, old_table_size, new_table, i, forwarder) - # transiently use a locked forwarding node - locked_forwarder = Node.new(moved_locked_hash = MOVED | LOCKED, new_table, NULL) - if old_table.cas(i, nil, locked_forwarder) - new_table.volatile_set(i, nil) # kill the potential reverse forwarders - new_table.volatile_set(i + old_table_size, nil) # kill the potential reverse forwarders - old_table.volatile_set(i, forwarder) - locked_forwarder.unlock_via_hash(moved_locked_hash, MOVED) - true - end - end - - # Splits a normal bin with list headed by e into lo and hi parts; installs in given table. - def split_old_bin(table, new_table, i, node, node_hash, forwarder) - table.try_lock_via_hash(i, node, node_hash) do - split_bin(new_table, i, node, node_hash) - table.volatile_set(i, forwarder) - end - end - - def split_bin(new_table, i, node, node_hash) - bit = new_table.size >> 1 # bit to split on - run_bit = node_hash & bit - last_run = nil - low = nil - high = nil - current_node = node - # this optimises for the lowest amount of volatile writes and objects created - while current_node = current_node.next - unless (b = current_node.hash & bit) == run_bit - run_bit = b - last_run = current_node - end - end - if run_bit == 0 - low = last_run - else - high = last_run - end - current_node = node - until current_node == last_run - pure_hash = current_node.pure_hash - if (pure_hash & bit) == 0 - low = Node.new(pure_hash, current_node.key, current_node.value, low) - else - high = Node.new(pure_hash, current_node.key, current_node.value, high) - end - current_node = current_node.next - end - new_table.volatile_set(i, low) - new_table.volatile_set(i + bit, high) - end - - def increment_size - @counter.increment - end - - def decrement_size(by = 1) - @counter.add(-by) - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/mri_map_backend.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -require 'thread' -require 'concurrent/collection/map/non_concurrent_map_backend' - -module Concurrent - - # @!visibility private - module Collection - - # @!visibility private - class MriMapBackend < NonConcurrentMapBackend - - def initialize(options = nil) - super(options) - @write_lock = Mutex.new - end - - def []=(key, value) - @write_lock.synchronize { super } - end - - def compute_if_absent(key) - if stored_value = _get(key) # fast non-blocking path for the most likely case - stored_value - else - @write_lock.synchronize { super } - end - end - - def compute_if_present(key) - @write_lock.synchronize { super } - end - - def compute(key) - @write_lock.synchronize { super } - end - - def merge_pair(key, value) - @write_lock.synchronize { super } - end - - def replace_pair(key, old_value, new_value) - @write_lock.synchronize { super } - end - - def replace_if_exists(key, new_value) - @write_lock.synchronize { super } - end - - def get_and_set(key, value) - @write_lock.synchronize { super } - end - - def delete(key) - @write_lock.synchronize { super } - end - - def delete_pair(key, value) - @write_lock.synchronize { super } - end - - def clear - @write_lock.synchronize { super } - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/non_concurrent_map_backend.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -require 'concurrent/constants' - -module Concurrent - - # @!visibility private - module Collection - - # @!visibility private - class NonConcurrentMapBackend - - # WARNING: all public methods of the class must operate on the @backend - # directly without calling each other. This is important because of the - # SynchronizedMapBackend which uses a non-reentrant mutex for performance - # reasons. - def initialize(options = nil) - @backend = {} - end - - def [](key) - @backend[key] - end - - def []=(key, value) - @backend[key] = value - end - - def compute_if_absent(key) - if NULL != (stored_value = @backend.fetch(key, NULL)) - stored_value - else - @backend[key] = yield - end - end - - def replace_pair(key, old_value, new_value) - if pair?(key, old_value) - @backend[key] = new_value - true - else - false - end - end - - def replace_if_exists(key, new_value) - if NULL != (stored_value = @backend.fetch(key, NULL)) - @backend[key] = new_value - stored_value - end - end - - def compute_if_present(key) - if NULL != (stored_value = @backend.fetch(key, NULL)) - store_computed_value(key, yield(stored_value)) - end - end - - def compute(key) - store_computed_value(key, yield(@backend[key])) - end - - def merge_pair(key, value) - if NULL == (stored_value = @backend.fetch(key, NULL)) - @backend[key] = value - else - store_computed_value(key, yield(stored_value)) - end - end - - def get_and_set(key, value) - stored_value = @backend[key] - @backend[key] = value - stored_value - end - - def key?(key) - @backend.key?(key) - end - - def delete(key) - @backend.delete(key) - end - - def delete_pair(key, value) - if pair?(key, value) - @backend.delete(key) - true - else - false - end - end - - def clear - @backend.clear - self - end - - def each_pair - dupped_backend.each_pair do |k, v| - yield k, v - end - self - end - - def size - @backend.size - end - - def get_or_default(key, default_value) - @backend.fetch(key, default_value) - end - - alias_method :_get, :[] - alias_method :_set, :[]= - private :_get, :_set - private - def initialize_copy(other) - super - @backend = {} - self - end - - def dupped_backend - @backend.dup - end - - def pair?(key, expected_value) - NULL != (stored_value = @backend.fetch(key, NULL)) && expected_value.equal?(stored_value) - end - - def store_computed_value(key, new_value) - if new_value.nil? - @backend.delete(key) - nil - else - @backend[key] = new_value - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/map/synchronized_map_backend.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -require 'concurrent/collection/map/non_concurrent_map_backend' - -module Concurrent - - # @!visibility private - module Collection - - # @!visibility private - class SynchronizedMapBackend < NonConcurrentMapBackend - - require 'mutex_m' - include Mutex_m - # WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are - # not allowed to call each other. - - def [](key) - synchronize { super } - end - - def []=(key, value) - synchronize { super } - end - - def compute_if_absent(key) - synchronize { super } - end - - def compute_if_present(key) - synchronize { super } - end - - def compute(key) - synchronize { super } - end - - def merge_pair(key, value) - synchronize { super } - end - - def replace_pair(key, old_value, new_value) - synchronize { super } - end - - def replace_if_exists(key, new_value) - synchronize { super } - end - - def get_and_set(key, value) - synchronize { super } - end - - def key?(key) - synchronize { super } - end - - def delete(key) - synchronize { super } - end - - def delete_pair(key, value) - synchronize { super } - end - - def clear - synchronize { super } - end - - def size - synchronize { super } - end - - def get_or_default(key, default_value) - synchronize { super } - end - - private - def dupped_backend - synchronize { super } - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/non_concurrent_priority_queue.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -require 'concurrent/collection/java_non_concurrent_priority_queue' -require 'concurrent/collection/ruby_non_concurrent_priority_queue' -require 'concurrent/utility/engine' - -module Concurrent - module Collection - - # @!visibility private - # @!macro internal_implementation_note - NonConcurrentPriorityQueueImplementation = case - when Concurrent.on_jruby? - JavaNonConcurrentPriorityQueue - else - RubyNonConcurrentPriorityQueue - end - private_constant :NonConcurrentPriorityQueueImplementation - - # @!macro priority_queue - # - # A queue collection in which the elements are sorted based on their - # comparison (spaceship) operator `<=>`. Items are added to the queue - # at a position relative to their priority. On removal the element - # with the "highest" priority is removed. By default the sort order is - # from highest to lowest, but a lowest-to-highest sort order can be - # set on construction. - # - # The API is based on the `Queue` class from the Ruby standard library. - # - # The pure Ruby implementation, `RubyNonConcurrentPriorityQueue` uses a heap algorithm - # stored in an array. The algorithm is based on the work of Robert Sedgewick - # and Kevin Wayne. - # - # The JRuby native implementation is a thin wrapper around the standard - # library `java.util.NonConcurrentPriorityQueue`. - # - # When running under JRuby the class `NonConcurrentPriorityQueue` extends `JavaNonConcurrentPriorityQueue`. - # When running under all other interpreters it extends `RubyNonConcurrentPriorityQueue`. - # - # @note This implementation is *not* thread safe. - # - # @see http://en.wikipedia.org/wiki/Priority_queue - # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html - # - # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6 - # @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html - # - # @!visibility private - class NonConcurrentPriorityQueue < NonConcurrentPriorityQueueImplementation - - alias_method :has_priority?, :include? - - alias_method :size, :length - - alias_method :deq, :pop - alias_method :shift, :pop - - alias_method :<<, :push - alias_method :enq, :push - - # @!method initialize(opts = {}) - # @!macro priority_queue_method_initialize - # - # Create a new priority queue with no items. - # - # @param [Hash] opts the options for creating the queue - # @option opts [Symbol] :order (:max) dictates the order in which items are - # stored: from highest to lowest when `:max` or `:high`; from lowest to - # highest when `:min` or `:low` - - # @!method clear - # @!macro priority_queue_method_clear - # - # Removes all of the elements from this priority queue. - - # @!method delete(item) - # @!macro priority_queue_method_delete - # - # Deletes all items from `self` that are equal to `item`. - # - # @param [Object] item the item to be removed from the queue - # @return [Object] true if the item is found else false - - # @!method empty? - # @!macro priority_queue_method_empty - # - # Returns `true` if `self` contains no elements. - # - # @return [Boolean] true if there are no items in the queue else false - - # @!method include?(item) - # @!macro priority_queue_method_include - # - # Returns `true` if the given item is present in `self` (that is, if any - # element == `item`), otherwise returns false. - # - # @param [Object] item the item to search for - # - # @return [Boolean] true if the item is found else false - - # @!method length - # @!macro priority_queue_method_length - # - # The current length of the queue. - # - # @return [Fixnum] the number of items in the queue - - # @!method peek - # @!macro priority_queue_method_peek - # - # Retrieves, but does not remove, the head of this queue, or returns `nil` - # if this queue is empty. - # - # @return [Object] the head of the queue or `nil` when empty - - # @!method pop - # @!macro priority_queue_method_pop - # - # Retrieves and removes the head of this queue, or returns `nil` if this - # queue is empty. - # - # @return [Object] the head of the queue or `nil` when empty - - # @!method push(item) - # @!macro priority_queue_method_push - # - # Inserts the specified element into this priority queue. - # - # @param [Object] item the item to insert onto the queue - - # @!method self.from_list(list, opts = {}) - # @!macro priority_queue_method_from_list - # - # Create a new priority queue from the given list. - # - # @param [Enumerable] list the list to build the queue from - # @param [Hash] opts the options for creating the queue - # - # @return [NonConcurrentPriorityQueue] the newly created and populated queue - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,150 +0,0 @@ -module Concurrent - module Collection - - # @!macro priority_queue - # - # @!visibility private - # @!macro internal_implementation_note - class RubyNonConcurrentPriorityQueue - - # @!macro priority_queue_method_initialize - def initialize(opts = {}) - order = opts.fetch(:order, :max) - @comparator = [:min, :low].include?(order) ? -1 : 1 - clear - end - - # @!macro priority_queue_method_clear - def clear - @queue = [nil] - @length = 0 - true - end - - # @!macro priority_queue_method_delete - def delete(item) - return false if empty? - original_length = @length - k = 1 - while k <= @length - if @queue[k] == item - swap(k, @length) - @length -= 1 - sink(k) - @queue.pop - else - k += 1 - end - end - @length != original_length - end - - # @!macro priority_queue_method_empty - def empty? - size == 0 - end - - # @!macro priority_queue_method_include - def include?(item) - @queue.include?(item) - end - alias_method :has_priority?, :include? - - # @!macro priority_queue_method_length - def length - @length - end - alias_method :size, :length - - # @!macro priority_queue_method_peek - def peek - empty? ? nil : @queue[1] - end - - # @!macro priority_queue_method_pop - def pop - return nil if empty? - max = @queue[1] - swap(1, @length) - @length -= 1 - sink(1) - @queue.pop - max - end - alias_method :deq, :pop - alias_method :shift, :pop - - # @!macro priority_queue_method_push - def push(item) - raise ArgumentError.new('cannot enqueue nil') if item.nil? - @length += 1 - @queue << item - swim(@length) - true - end - alias_method :<<, :push - alias_method :enq, :push - - # @!macro priority_queue_method_from_list - def self.from_list(list, opts = {}) - queue = new(opts) - list.each{|item| queue << item } - queue - end - - private - - # Exchange the values at the given indexes within the internal array. - # - # @param [Integer] x the first index to swap - # @param [Integer] y the second index to swap - # - # @!visibility private - def swap(x, y) - temp = @queue[x] - @queue[x] = @queue[y] - @queue[y] = temp - end - - # Are the items at the given indexes ordered based on the priority - # order specified at construction? - # - # @param [Integer] x the first index from which to retrieve a comparable value - # @param [Integer] y the second index from which to retrieve a comparable value - # - # @return [Boolean] true if the two elements are in the correct priority order - # else false - # - # @!visibility private - def ordered?(x, y) - (@queue[x] <=> @queue[y]) == @comparator - end - - # Percolate down to maintain heap invariant. - # - # @param [Integer] k the index at which to start the percolation - # - # @!visibility private - def sink(k) - while (j = (2 * k)) <= @length do - j += 1 if j < @length && ! ordered?(j, j+1) - break if ordered?(k, j) - swap(k, j) - k = j - end - end - - # Percolate up to maintain heap invariant. - # - # @param [Integer] k the index at which to start the percolation - # - # @!visibility private - def swim(k) - while k > 1 && ! ordered?(k/2, k) do - swap(k, k/2) - k = k/2 - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/deprecation.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -require 'concurrent/concern/logging' - -module Concurrent - module Concern - - # @!visibility private - # @!macro internal_implementation_note - module Deprecation - # TODO require additional parameter: a version. Display when it'll be removed based on that. Error if not removed. - include Concern::Logging - - def deprecated(message, strip = 2) - caller_line = caller(strip).first if strip > 0 - klass = if Module === self - self - else - self.class - end - message = if strip > 0 - format("[DEPRECATED] %s\ncalled on: %s", message, caller_line) - else - format('[DEPRECATED] %s', message) - end - log WARN, klass.to_s, message - end - - def deprecated_method(old_name, new_name) - deprecated "`#{old_name}` is deprecated and it'll removed in next release, use `#{new_name}` instead", 3 - end - - extend self - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/dereferenceable.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -module Concurrent - module Concern - - # Object references in Ruby are mutable. This can lead to serious problems when - # the `#value` of a concurrent object is a mutable reference. Which is always the - # case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type. - # Most classes in this library that expose a `#value` getter method do so using the - # `Dereferenceable` mixin module. - # - # @!macro copy_options - module Dereferenceable - # NOTE: This module is going away in 2.0. In the mean time we need it to - # play nicely with the synchronization layer. This means that the - # including class SHOULD be synchronized and it MUST implement a - # `#synchronize` method. Not doing so will lead to runtime errors. - - # Return the value this object represents after applying the options specified - # by the `#set_deref_options` method. - # - # @return [Object] the current value of the object - def value - synchronize { apply_deref_options(@value) } - end - alias_method :deref, :value - - protected - - # Set the internal value of this object - # - # @param [Object] value the new value - def value=(value) - synchronize{ @value = value } - end - - # @!macro dereferenceable_set_deref_options - # Set the options which define the operations #value performs before - # returning data to the caller (dereferencing). - # - # @note Most classes that include this module will call `#set_deref_options` - # from within the constructor, thus allowing these options to be set at - # object creation. - # - # @param [Hash] opts the options defining dereference behavior. - # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data - # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data - # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing - # the internal value and returning the value returned from the proc - def set_deref_options(opts = {}) - synchronize{ ns_set_deref_options(opts) } - end - - # @!macro dereferenceable_set_deref_options - # @!visibility private - def ns_set_deref_options(opts) - @dup_on_deref = opts[:dup_on_deref] || opts[:dup] - @freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze] - @copy_on_deref = opts[:copy_on_deref] || opts[:copy] - @do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref) - nil - end - - # @!visibility private - def apply_deref_options(value) - return nil if value.nil? - return value if @do_nothing_on_deref - value = @copy_on_deref.call(value) if @copy_on_deref - value = value.dup if @dup_on_deref - value = value.freeze if @freeze_on_deref - value - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/logging.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -require 'logger' - -module Concurrent - module Concern - - # Include where logging is needed - # - # @!visibility private - module Logging - include Logger::Severity - - # Logs through {Concurrent.global_logger}, it can be overridden by setting @logger - # @param [Integer] level one of Logger::Severity constants - # @param [String] progname e.g. a path of an Actor - # @param [String, nil] message when nil block is used to generate the message - # @yieldreturn [String] a message - def log(level, progname, message = nil, &block) - #NOTE: Cannot require 'concurrent/configuration' above due to circular references. - # Assume that the gem has been initialized if we've gotten this far. - logger = if defined?(@logger) && @logger - @logger - else - Concurrent.global_logger - end - logger.call level, progname, message, &block - rescue => error - $stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" + - "#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}" - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/obligation.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,220 +0,0 @@ -require 'thread' -require 'timeout' - -require 'concurrent/atomic/event' -require 'concurrent/concern/dereferenceable' - -module Concurrent - module Concern - - module Obligation - include Concern::Dereferenceable - # NOTE: The Dereferenceable module is going away in 2.0. In the mean time - # we need it to place nicely with the synchronization layer. This means - # that the including class SHOULD be synchronized and it MUST implement a - # `#synchronize` method. Not doing so will lead to runtime errors. - - # Has the obligation been fulfilled? - # - # @return [Boolean] - def fulfilled? - state == :fulfilled - end - alias_method :realized?, :fulfilled? - - # Has the obligation been rejected? - # - # @return [Boolean] - def rejected? - state == :rejected - end - - # Is obligation completion still pending? - # - # @return [Boolean] - def pending? - state == :pending - end - - # Is the obligation still unscheduled? - # - # @return [Boolean] - def unscheduled? - state == :unscheduled - end - - # Has the obligation completed processing? - # - # @return [Boolean] - def complete? - [:fulfilled, :rejected].include? state - end - - # Is the obligation still awaiting completion of processing? - # - # @return [Boolean] - def incomplete? - ! complete? - end - - # The current value of the obligation. Will be `nil` while the state is - # pending or the operation has been rejected. - # - # @param [Numeric] timeout the maximum time in seconds to wait. - # @return [Object] see Dereferenceable#deref - def value(timeout = nil) - wait timeout - deref - end - - # Wait until obligation is complete or the timeout has been reached. - # - # @param [Numeric] timeout the maximum time in seconds to wait. - # @return [Obligation] self - def wait(timeout = nil) - event.wait(timeout) if timeout != 0 && incomplete? - self - end - - # Wait until obligation is complete or the timeout is reached. Will re-raise - # any exceptions raised during processing (but will not raise an exception - # on timeout). - # - # @param [Numeric] timeout the maximum time in seconds to wait. - # @return [Obligation] self - # @raise [Exception] raises the reason when rejected - def wait!(timeout = nil) - wait(timeout).tap { raise self if rejected? } - end - alias_method :no_error!, :wait! - - # The current value of the obligation. Will be `nil` while the state is - # pending or the operation has been rejected. Will re-raise any exceptions - # raised during processing (but will not raise an exception on timeout). - # - # @param [Numeric] timeout the maximum time in seconds to wait. - # @return [Object] see Dereferenceable#deref - # @raise [Exception] raises the reason when rejected - def value!(timeout = nil) - wait(timeout) - if rejected? - raise self - else - deref - end - end - - # The current state of the obligation. - # - # @return [Symbol] the current state - def state - synchronize { @state } - end - - # If an exception was raised during processing this will return the - # exception object. Will return `nil` when the state is pending or if - # the obligation has been successfully fulfilled. - # - # @return [Exception] the exception raised during processing or `nil` - def reason - synchronize { @reason } - end - - # @example allows Obligation to be risen - # rejected_ivar = Ivar.new.fail - # raise rejected_ivar - def exception(*args) - raise 'obligation is not rejected' unless rejected? - reason.exception(*args) - end - - protected - - # @!visibility private - def get_arguments_from(opts = {}) - [*opts.fetch(:args, [])] - end - - # @!visibility private - def init_obligation - @event = Event.new - @value = @reason = nil - end - - # @!visibility private - def event - @event - end - - # @!visibility private - def set_state(success, value, reason) - if success - @value = value - @state = :fulfilled - else - @reason = reason - @state = :rejected - end - end - - # @!visibility private - def state=(value) - synchronize { ns_set_state(value) } - end - - # Atomic compare and set operation - # State is set to `next_state` only if `current state == expected_current`. - # - # @param [Symbol] next_state - # @param [Symbol] expected_current - # - # @return [Boolean] true is state is changed, false otherwise - # - # @!visibility private - def compare_and_set_state(next_state, *expected_current) - synchronize do - if expected_current.include? @state - @state = next_state - true - else - false - end - end - end - - # Executes the block within mutex if current state is included in expected_states - # - # @return block value if executed, false otherwise - # - # @!visibility private - def if_state(*expected_states) - synchronize do - raise ArgumentError.new('no block given') unless block_given? - - if expected_states.include? @state - yield - else - false - end - end - end - - protected - - # Am I in the current state? - # - # @param [Symbol] expected The state to check against - # @return [Boolean] true if in the expected state else false - # - # @!visibility private - def ns_check_state?(expected) - @state == expected - end - - # @!visibility private - def ns_set_state(value) - @state = value - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concern/observable.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -require 'concurrent/collection/copy_on_notify_observer_set' -require 'concurrent/collection/copy_on_write_observer_set' - -module Concurrent - module Concern - - # The [observer pattern](http://en.wikipedia.org/wiki/Observer_pattern) is one - # of the most useful design patterns. - # - # The workflow is very simple: - # - an `observer` can register itself to a `subject` via a callback - # - many `observers` can be registered to the same `subject` - # - the `subject` notifies all registered observers when its status changes - # - an `observer` can deregister itself when is no more interested to receive - # event notifications - # - # In a single threaded environment the whole pattern is very easy: the - # `subject` can use a simple data structure to manage all its subscribed - # `observer`s and every `observer` can react directly to every event without - # caring about synchronization. - # - # In a multi threaded environment things are more complex. The `subject` must - # synchronize the access to its data structure and to do so currently we're - # using two specialized ObserverSet: {Concurrent::Concern::CopyOnWriteObserverSet} - # and {Concurrent::Concern::CopyOnNotifyObserverSet}. - # - # When implementing and `observer` there's a very important rule to remember: - # **there are no guarantees about the thread that will execute the callback** - # - # Let's take this example - # ``` - # class Observer - # def initialize - # @count = 0 - # end - # - # def update - # @count += 1 - # end - # end - # - # obs = Observer.new - # [obj1, obj2, obj3, obj4].each { |o| o.add_observer(obs) } - # # execute [obj1, obj2, obj3, obj4] - # ``` - # - # `obs` is wrong because the variable `@count` can be accessed by different - # threads at the same time, so it should be synchronized (using either a Mutex - # or an AtomicFixum) - module Observable - - # @!macro observable_add_observer - # - # Adds an observer to this set. If a block is passed, the observer will be - # created by this method and no other params should be passed. - # - # @param [Object] observer the observer to add - # @param [Symbol] func the function to call on the observer during notification. - # Default is :update - # @return [Object] the added observer - def add_observer(observer = nil, func = :update, &block) - observers.add_observer(observer, func, &block) - end - - # As `#add_observer` but can be used for chaining. - # - # @param [Object] observer the observer to add - # @param [Symbol] func the function to call on the observer during notification. - # @return [Observable] self - def with_observer(observer = nil, func = :update, &block) - add_observer(observer, func, &block) - self - end - - # @!macro observable_delete_observer - # - # Remove `observer` as an observer on this object so that it will no - # longer receive notifications. - # - # @param [Object] observer the observer to remove - # @return [Object] the deleted observer - def delete_observer(observer) - observers.delete_observer(observer) - end - - # @!macro observable_delete_observers - # - # Remove all observers associated with this object. - # - # @return [Observable] self - def delete_observers - observers.delete_observers - self - end - - # @!macro observable_count_observers - # - # Return the number of observers associated with this object. - # - # @return [Integer] the observers count - def count_observers - observers.count_observers - end - - protected - - attr_accessor :observers - end - end -end Binary files /tmp/tmphdj9sviw/x9hvePX3m4/puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concurrent_ruby.jar and /tmp/tmphdj9sviw/RYzLS_laiZ/puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/concurrent_ruby.jar differ diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/configuration.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,184 +0,0 @@ -require 'thread' -require 'concurrent/delay' -require 'concurrent/errors' -require 'concurrent/atomic/atomic_reference' -require 'concurrent/concern/logging' -require 'concurrent/executor/immediate_executor' -require 'concurrent/executor/cached_thread_pool' -require 'concurrent/utility/at_exit' -require 'concurrent/utility/processor_counter' - -module Concurrent - extend Concern::Logging - - autoload :Options, 'concurrent/options' - autoload :TimerSet, 'concurrent/executor/timer_set' - autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor' - - # @return [Logger] Logger with provided level and output. - def self.create_simple_logger(level = Logger::FATAL, output = $stderr) - # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking - lambda do |severity, progname, message = nil, &block| - return false if severity < level - - message = block ? block.call : message - formatted_message = case message - when String - message - when Exception - format "%s (%s)\n%s", - message.message, message.class, (message.backtrace || []).join("\n") - else - message.inspect - end - - output.print format "[%s] %5s -- %s: %s\n", - Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'), - Logger::SEV_LABEL[severity], - progname, - formatted_message - true - end - end - - # Use logger created by #create_simple_logger to log concurrent-ruby messages. - def self.use_simple_logger(level = Logger::FATAL, output = $stderr) - Concurrent.global_logger = create_simple_logger level, output - end - - # @return [Logger] Logger with provided level and output. - # @deprecated - def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr) - logger = Logger.new(output) - logger.level = level - logger.formatter = lambda do |severity, datetime, progname, msg| - formatted_message = case msg - when String - msg - when Exception - format "%s (%s)\n%s", - msg.message, msg.class, (msg.backtrace || []).join("\n") - else - msg.inspect - end - format "[%s] %5s -- %s: %s\n", - datetime.strftime('%Y-%m-%d %H:%M:%S.%L'), - severity, - progname, - formatted_message - end - - lambda do |loglevel, progname, message = nil, &block| - logger.add loglevel, message, progname, &block - end - end - - # Use logger created by #create_stdlib_logger to log concurrent-ruby messages. - # @deprecated - def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr) - Concurrent.global_logger = create_stdlib_logger level, output - end - - # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods - - # Suppresses all output when used for logging. - NULL_LOGGER = lambda { |level, progname, message = nil, &block| } - - # @!visibility private - GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN)) - private_constant :GLOBAL_LOGGER - - def self.global_logger - GLOBAL_LOGGER.value - end - - def self.global_logger=(value) - GLOBAL_LOGGER.value = value - end - - # @!visibility private - GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor(auto_terminate: true) } - private_constant :GLOBAL_FAST_EXECUTOR - - # @!visibility private - GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor(auto_terminate: true) } - private_constant :GLOBAL_IO_EXECUTOR - - # @!visibility private - GLOBAL_TIMER_SET = Delay.new { TimerSet.new(auto_terminate: true) } - private_constant :GLOBAL_TIMER_SET - - # @!visibility private - GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new - private_constant :GLOBAL_IMMEDIATE_EXECUTOR - - # Disables AtExit handlers including pool auto-termination handlers. - # When disabled it will be the application programmer's responsibility - # to ensure that the handlers are shutdown properly prior to application - # exit by calling {AtExit.run} method. - # - # @note this option should be needed only because of `at_exit` ordering - # issues which may arise when running some of the testing frameworks. - # E.g. Minitest's test-suite runs itself in `at_exit` callback which - # executes after the pools are already terminated. Then auto termination - # needs to be disabled and called manually after test-suite ends. - # @note This method should *never* be called - # from within a gem. It should *only* be used from within the main - # application and even then it should be used only when necessary. - # @see AtExit - def self.disable_at_exit_handlers! - AtExit.enabled = false - end - - # Global thread pool optimized for short, fast *operations*. - # - # @return [ThreadPoolExecutor] the thread pool - def self.global_fast_executor - GLOBAL_FAST_EXECUTOR.value - end - - # Global thread pool optimized for long, blocking (IO) *tasks*. - # - # @return [ThreadPoolExecutor] the thread pool - def self.global_io_executor - GLOBAL_IO_EXECUTOR.value - end - - def self.global_immediate_executor - GLOBAL_IMMEDIATE_EXECUTOR - end - - # Global thread pool user for global *timers*. - # - # @return [Concurrent::TimerSet] the thread pool - def self.global_timer_set - GLOBAL_TIMER_SET.value - end - - # General access point to global executors. - # @param [Symbol, Executor] executor_identifier symbols: - # - :fast - {Concurrent.global_fast_executor} - # - :io - {Concurrent.global_io_executor} - # - :immediate - {Concurrent.global_immediate_executor} - # @return [Executor] - def self.executor(executor_identifier) - Options.executor(executor_identifier) - end - - def self.new_fast_executor(opts = {}) - FixedThreadPool.new( - [2, Concurrent.processor_count].max, - auto_terminate: opts.fetch(:auto_terminate, true), - idletime: 60, # 1 minute - max_queue: 0, # unlimited - fallback_policy: :abort # shouldn't matter -- 0 max queue - ) - end - - def self.new_io_executor(opts = {}) - CachedThreadPool.new( - auto_terminate: opts.fetch(:auto_terminate, true), - fallback_policy: :abort # shouldn't matter -- 0 max queue - ) - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/constants.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -module Concurrent - - # Various classes within allows for +nil+ values to be stored, - # so a special +NULL+ token is required to indicate the "nil-ness". - # @!visibility private - NULL = ::Object.new - -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/dataflow.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -require 'concurrent/future' -require 'concurrent/atomic/atomic_fixnum' - -module Concurrent - - # @!visibility private - class DependencyCounter # :nodoc: - - def initialize(count, &block) - @counter = AtomicFixnum.new(count) - @block = block - end - - def update(time, value, reason) - if @counter.decrement == 0 - @block.call - end - end - end - - # Dataflow allows you to create a task that will be scheduled when all of its data dependencies are available. - # {include:file:docs-source/dataflow.md} - # - # @param [Future] inputs zero or more `Future` operations that this dataflow depends upon - # - # @yield The operation to perform once all the dependencies are met - # @yieldparam [Future] inputs each of the `Future` inputs to the dataflow - # @yieldreturn [Object] the result of the block operation - # - # @return [Object] the result of all the operations - # - # @raise [ArgumentError] if no block is given - # @raise [ArgumentError] if any of the inputs are not `IVar`s - def dataflow(*inputs, &block) - dataflow_with(Concurrent.global_io_executor, *inputs, &block) - end - module_function :dataflow - - def dataflow_with(executor, *inputs, &block) - call_dataflow(:value, executor, *inputs, &block) - end - module_function :dataflow_with - - def dataflow!(*inputs, &block) - dataflow_with!(Concurrent.global_io_executor, *inputs, &block) - end - module_function :dataflow! - - def dataflow_with!(executor, *inputs, &block) - call_dataflow(:value!, executor, *inputs, &block) - end - module_function :dataflow_with! - - private - - def call_dataflow(method, executor, *inputs, &block) - raise ArgumentError.new('an executor must be provided') if executor.nil? - raise ArgumentError.new('no block given') unless block_given? - unless inputs.all? { |input| input.is_a? IVar } - raise ArgumentError.new("Not all dependencies are IVars.\nDependencies: #{ inputs.inspect }") - end - - result = Future.new(executor: executor) do - values = inputs.map { |input| input.send(method) } - block.call(*values) - end - - if inputs.empty? - result.execute - else - counter = DependencyCounter.new(inputs.size) { result.execute } - - inputs.each do |input| - input.add_observer counter - end - end - - result - end - module_function :call_dataflow -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/delay.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,199 +0,0 @@ -require 'thread' -require 'concurrent/concern/obligation' -require 'concurrent/executor/immediate_executor' -require 'concurrent/synchronization' - -module Concurrent - - # This file has circular require issues. It must be autoloaded here. - autoload :Options, 'concurrent/options' - - # Lazy evaluation of a block yielding an immutable result. Useful for - # expensive operations that may never be needed. It may be non-blocking, - # supports the `Concern::Obligation` interface, and accepts the injection of - # custom executor upon which to execute the block. Processing of - # block will be deferred until the first time `#value` is called. - # At that time the caller can choose to return immediately and let - # the block execute asynchronously, block indefinitely, or block - # with a timeout. - # - # When a `Delay` is created its state is set to `pending`. The value and - # reason are both `nil`. The first time the `#value` method is called the - # enclosed opration will be run and the calling thread will block. Other - # threads attempting to call `#value` will block as well. Once the operation - # is complete the *value* will be set to the result of the operation or the - # *reason* will be set to the raised exception, as appropriate. All threads - # blocked on `#value` will return. Subsequent calls to `#value` will immediately - # return the cached value. The operation will only be run once. This means that - # any side effects created by the operation will only happen once as well. - # - # `Delay` includes the `Concurrent::Concern::Dereferenceable` mixin to support thread - # safety of the reference returned by `#value`. - # - # @!macro copy_options - # - # @!macro delay_note_regarding_blocking - # @note The default behavior of `Delay` is to block indefinitely when - # calling either `value` or `wait`, executing the delayed operation on - # the current thread. This makes the `timeout` value completely - # irrelevant. To enable non-blocking behavior, use the `executor` - # constructor option. This will cause the delayed operation to be - # execute on the given executor, allowing the call to timeout. - # - # @see Concurrent::Concern::Dereferenceable - class Delay < Synchronization::LockableObject - include Concern::Obligation - - # NOTE: Because the global thread pools are lazy-loaded with these objects - # there is a performance hit every time we post a new task to one of these - # thread pools. Subsequently it is critical that `Delay` perform as fast - # as possible post-completion. This class has been highly optimized using - # the benchmark script `examples/lazy_and_delay.rb`. Do NOT attempt to - # DRY-up this class or perform other refactoring with running the - # benchmarks and ensuring that performance is not negatively impacted. - - # Create a new `Delay` in the `:pending` state. - # - # @!macro executor_and_deref_options - # - # @yield the delayed operation to perform - # - # @raise [ArgumentError] if no block is given - def initialize(opts = {}, &block) - raise ArgumentError.new('no block given') unless block_given? - super(&nil) - synchronize { ns_initialize(opts, &block) } - end - - # Return the value this object represents after applying the options - # specified by the `#set_deref_options` method. If the delayed operation - # raised an exception this method will return nil. The execption object - # can be accessed via the `#reason` method. - # - # @param [Numeric] timeout the maximum number of seconds to wait - # @return [Object] the current value of the object - # - # @!macro delay_note_regarding_blocking - def value(timeout = nil) - if @executor # TODO (pitr 12-Sep-2015): broken unsafe read? - super - else - # this function has been optimized for performance and - # should not be modified without running new benchmarks - synchronize do - execute = @evaluation_started = true unless @evaluation_started - if execute - begin - set_state(true, @task.call, nil) - rescue => ex - set_state(false, nil, ex) - end - elsif incomplete? - raise IllegalOperationError, 'Recursive call to #value during evaluation of the Delay' - end - end - if @do_nothing_on_deref - @value - else - apply_deref_options(@value) - end - end - end - - # Return the value this object represents after applying the options - # specified by the `#set_deref_options` method. If the delayed operation - # raised an exception, this method will raise that exception (even when) - # the operation has already been executed). - # - # @param [Numeric] timeout the maximum number of seconds to wait - # @return [Object] the current value of the object - # @raise [Exception] when `#rejected?` raises `#reason` - # - # @!macro delay_note_regarding_blocking - def value!(timeout = nil) - if @executor - super - else - result = value - raise @reason if @reason - result - end - end - - # Return the value this object represents after applying the options - # specified by the `#set_deref_options` method. - # - # @param [Integer] timeout (nil) the maximum number of seconds to wait for - # the value to be computed. When `nil` the caller will block indefinitely. - # - # @return [Object] self - # - # @!macro delay_note_regarding_blocking - def wait(timeout = nil) - if @executor - execute_task_once - super(timeout) - else - value - end - self - end - - # Reconfigures the block returning the value if still `#incomplete?` - # - # @yield the delayed operation to perform - # @return [true, false] if success - def reconfigure(&block) - synchronize do - raise ArgumentError.new('no block given') unless block_given? - unless @evaluation_started - @task = block - true - else - false - end - end - end - - protected - - def ns_initialize(opts, &block) - init_obligation - set_deref_options(opts) - @executor = opts[:executor] - - @task = block - @state = :pending - @evaluation_started = false - end - - private - - # @!visibility private - def execute_task_once # :nodoc: - # this function has been optimized for performance and - # should not be modified without running new benchmarks - execute = task = nil - synchronize do - execute = @evaluation_started = true unless @evaluation_started - task = @task - end - - if execute - executor = Options.executor_from_options(executor: @executor) - executor.post do - begin - result = task.call - success = true - rescue => ex - reason = ex - end - synchronize do - set_state(success, result, reason) - event.set - end - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/errors.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -module Concurrent - - Error = Class.new(StandardError) - - # Raised when errors occur during configuration. - ConfigurationError = Class.new(Error) - - # Raised when an asynchronous operation is cancelled before execution. - CancelledOperationError = Class.new(Error) - - # Raised when a lifecycle method (such as `stop`) is called in an improper - # sequence or when the object is in an inappropriate state. - LifecycleError = Class.new(Error) - - # Raised when an attempt is made to violate an immutability guarantee. - ImmutabilityError = Class.new(Error) - - # Raised when an operation is attempted which is not legal given the - # receiver's current state - IllegalOperationError = Class.new(Error) - - # Raised when an object's methods are called when it has not been - # properly initialized. - InitializationError = Class.new(Error) - - # Raised when an object with a start/stop lifecycle has been started an - # excessive number of times. Often used in conjunction with a restart - # policy or strategy. - MaxRestartFrequencyError = Class.new(Error) - - # Raised when an attempt is made to modify an immutable object - # (such as an `IVar`) after its final state has been set. - class MultipleAssignmentError < Error - attr_reader :inspection_data - - def initialize(message = nil, inspection_data = nil) - @inspection_data = inspection_data - super message - end - - def inspect - format '%s %s>', super[0..-2], @inspection_data.inspect - end - end - - # Raised by an `Executor` when it is unable to process a given task, - # possibly because of a reject policy or other internal error. - RejectedExecutionError = Class.new(Error) - - # Raised when any finite resource, such as a lock counter, exceeds its - # maximum limit/threshold. - ResourceLimitError = Class.new(Error) - - # Raised when an operation times out. - TimeoutError = Class.new(Error) - - # Aggregates multiple exceptions. - class MultipleErrors < Error - attr_reader :errors - - def initialize(errors, message = "#{errors.size} errors") - @errors = errors - super [*message, - *errors.map { |e| [format('%s (%s)', e.message, e.class), *e.backtrace] }.flatten(1) - ].join("\n") - end - end - -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/exchanger.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,352 +0,0 @@ -require 'concurrent/constants' -require 'concurrent/errors' -require 'concurrent/maybe' -require 'concurrent/atomic/atomic_reference' -require 'concurrent/atomic/count_down_latch' -require 'concurrent/utility/engine' -require 'concurrent/utility/monotonic_time' - -module Concurrent - - # @!macro exchanger - # - # A synchronization point at which threads can pair and swap elements within - # pairs. Each thread presents some object on entry to the exchange method, - # matches with a partner thread, and receives its partner's object on return. - # - # @!macro thread_safe_variable_comparison - # - # This implementation is very simple, using only a single slot for each - # exchanger (unlike more advanced implementations which use an "arena"). - # This approach will work perfectly fine when there are only a few threads - # accessing a single `Exchanger`. Beyond a handful of threads the performance - # will degrade rapidly due to contention on the single slot, but the algorithm - # will remain correct. - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html java.util.concurrent.Exchanger - # @example - # - # exchanger = Concurrent::Exchanger.new - # - # threads = [ - # Thread.new { puts "first: " << exchanger.exchange('foo', 1) }, #=> "first: bar" - # Thread.new { puts "second: " << exchanger.exchange('bar', 1) } #=> "second: foo" - # ] - # threads.each {|t| t.join(2) } - - # @!visibility private - class AbstractExchanger < Synchronization::Object - - # @!visibility private - CANCEL = ::Object.new - private_constant :CANCEL - - def initialize - super - end - - # @!macro exchanger_method_do_exchange - # - # Waits for another thread to arrive at this exchange point (unless the - # current thread is interrupted), and then transfers the given object to - # it, receiving its object in return. The timeout value indicates the - # approximate number of seconds the method should block while waiting - # for the exchange. When the timeout value is `nil` the method will - # block indefinitely. - # - # @param [Object] value the value to exchange with another thread - # @param [Numeric, nil] timeout in seconds, `nil` blocks indefinitely - # - # @!macro exchanger_method_exchange - # - # In some edge cases when a `timeout` is given a return value of `nil` may be - # ambiguous. Specifically, if `nil` is a valid value in the exchange it will - # be impossible to tell whether `nil` is the actual return value or if it - # signifies timeout. When `nil` is a valid value in the exchange consider - # using {#exchange!} or {#try_exchange} instead. - # - # @return [Object] the value exchanged by the other thread or `nil` on timeout - def exchange(value, timeout = nil) - (value = do_exchange(value, timeout)) == CANCEL ? nil : value - end - - # @!macro exchanger_method_do_exchange - # @!macro exchanger_method_exchange_bang - # - # On timeout a {Concurrent::TimeoutError} exception will be raised. - # - # @return [Object] the value exchanged by the other thread - # @raise [Concurrent::TimeoutError] on timeout - def exchange!(value, timeout = nil) - if (value = do_exchange(value, timeout)) == CANCEL - raise Concurrent::TimeoutError - else - value - end - end - - # @!macro exchanger_method_do_exchange - # @!macro exchanger_method_try_exchange - # - # The return value will be a {Concurrent::Maybe} set to `Just` on success or - # `Nothing` on timeout. - # - # @return [Concurrent::Maybe] on success a `Just` maybe will be returned with - # the item exchanged by the other thread as `#value`; on timeout a - # `Nothing` maybe will be returned with {Concurrent::TimeoutError} as `#reason` - # - # @example - # - # exchanger = Concurrent::Exchanger.new - # - # result = exchanger.exchange(:foo, 0.5) - # - # if result.just? - # puts result.value #=> :bar - # else - # puts 'timeout' - # end - def try_exchange(value, timeout = nil) - if (value = do_exchange(value, timeout)) == CANCEL - Concurrent::Maybe.nothing(Concurrent::TimeoutError) - else - Concurrent::Maybe.just(value) - end - end - - private - - # @!macro exchanger_method_do_exchange - # - # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout - def do_exchange(value, timeout) - raise NotImplementedError - end - end - - # @!macro internal_implementation_note - # @!visibility private - class RubyExchanger < AbstractExchanger - # A simplified version of java.util.concurrent.Exchanger written by - # Doug Lea, Bill Scherer, and Michael Scott with assistance from members - # of JCP JSR-166 Expert Group and released to the public domain. It does - # not include the arena or the multi-processor spin loops. - # http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/Exchanger.java - - safe_initialization! - - class Node < Concurrent::Synchronization::Object - attr_atomic :value - safe_initialization! - - def initialize(item) - super() - @Item = item - @Latch = Concurrent::CountDownLatch.new - self.value = nil - end - - def latch - @Latch - end - - def item - @Item - end - end - private_constant :Node - - def initialize - super - end - - private - - attr_atomic(:slot) - - # @!macro exchanger_method_do_exchange - # - # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout - def do_exchange(value, timeout) - - # ALGORITHM - # - # From the original Java version: - # - # > The basic idea is to maintain a "slot", which is a reference to - # > a Node containing both an Item to offer and a "hole" waiting to - # > get filled in. If an incoming "occupying" thread sees that the - # > slot is null, it CAS'es (compareAndSets) a Node there and waits - # > for another to invoke exchange. That second "fulfilling" thread - # > sees that the slot is non-null, and so CASes it back to null, - # > also exchanging items by CASing the hole, plus waking up the - # > occupying thread if it is blocked. In each case CAS'es may - # > fail because a slot at first appears non-null but is null upon - # > CAS, or vice-versa. So threads may need to retry these - # > actions. - # - # This version: - # - # An exchange occurs between an "occupier" thread and a "fulfiller" thread. - # The "slot" is used to setup this interaction. The first thread in the - # exchange puts itself into the slot (occupies) and waits for a fulfiller. - # The second thread removes the occupier from the slot and attempts to - # perform the exchange. Removing the occupier also frees the slot for - # another occupier/fulfiller pair. - # - # Because the occupier and the fulfiller are operating independently and - # because there may be contention with other threads, any failed operation - # indicates contention. Both the occupier and the fulfiller operate within - # spin loops. Any failed actions along the happy path will cause the thread - # to repeat the loop and try again. - # - # When a timeout value is given the thread must be cognizant of time spent - # in the spin loop. The remaining time is checked every loop. When the time - # runs out the thread will exit. - # - # A "node" is the data structure used to perform the exchange. Only the - # occupier's node is necessary. It's the node used for the exchange. - # Each node has an "item," a "hole" (self), and a "latch." The item is the - # node's initial value. It never changes. It's what the fulfiller returns on - # success. The occupier's hole is where the fulfiller put its item. It's the - # item that the occupier returns on success. The latch is used for synchronization. - # Because a thread may act as either an occupier or fulfiller (or possibly - # both in periods of high contention) every thread creates a node when - # the exchange method is first called. - # - # The following steps occur within the spin loop. If any actions fail - # the thread will loop and try again, so long as there is time remaining. - # If time runs out the thread will return CANCEL. - # - # Check the slot for an occupier: - # - # * If the slot is empty try to occupy - # * If the slot is full try to fulfill - # - # Attempt to occupy: - # - # * Attempt to CAS myself into the slot - # * Go to sleep and wait to be woken by a fulfiller - # * If the sleep is successful then the fulfiller completed its happy path - # - Return the value from my hole (the value given by the fulfiller) - # * When the sleep fails (time ran out) attempt to cancel the operation - # - Attempt to CAS myself out of the hole - # - If successful there is no contention - # - Return CANCEL - # - On failure, I am competing with a fulfiller - # - Attempt to CAS my hole to CANCEL - # - On success - # - Let the fulfiller deal with my cancel - # - Return CANCEL - # - On failure the fulfiller has completed its happy path - # - Return th value from my hole (the fulfiller's value) - # - # Attempt to fulfill: - # - # * Attempt to CAS the occupier out of the slot - # - On failure loop again - # * Attempt to CAS my item into the occupier's hole - # - On failure the occupier is trying to cancel - # - Loop again - # - On success we are on the happy path - # - Wake the sleeping occupier - # - Return the occupier's item - - value = NULL if value.nil? # The sentinel allows nil to be a valid value - me = Node.new(value) # create my node in case I need to occupy - end_at = Concurrent.monotonic_time + timeout.to_f # The time to give up - - result = loop do - other = slot - if other && compare_and_set_slot(other, nil) - # try to fulfill - if other.compare_and_set_value(nil, value) - # happy path - other.latch.count_down - break other.item - end - elsif other.nil? && compare_and_set_slot(nil, me) - # try to occupy - timeout = end_at - Concurrent.monotonic_time if timeout - if me.latch.wait(timeout) - # happy path - break me.value - else - # attempt to remove myself from the slot - if compare_and_set_slot(me, nil) - break CANCEL - elsif !me.compare_and_set_value(nil, CANCEL) - # I've failed to block the fulfiller - break me.value - end - end - end - break CANCEL if timeout && Concurrent.monotonic_time >= end_at - end - - result == NULL ? nil : result - end - end - - if Concurrent.on_jruby? - - # @!macro internal_implementation_note - # @!visibility private - class JavaExchanger < AbstractExchanger - - def initialize - @exchanger = java.util.concurrent.Exchanger.new - end - - private - - # @!macro exchanger_method_do_exchange - # - # @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout - def do_exchange(value, timeout) - result = nil - if timeout.nil? - Synchronization::JRuby.sleep_interruptibly do - result = @exchanger.exchange(value) - end - else - Synchronization::JRuby.sleep_interruptibly do - result = @exchanger.exchange(value, 1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) - end - end - result - rescue java.util.concurrent.TimeoutException - CANCEL - end - end - end - - # @!visibility private - # @!macro internal_implementation_note - ExchangerImplementation = case - when Concurrent.on_jruby? - JavaExchanger - else - RubyExchanger - end - private_constant :ExchangerImplementation - - # @!macro exchanger - class Exchanger < ExchangerImplementation - - # @!method initialize - # Creates exchanger instance - - # @!method exchange(value, timeout = nil) - # @!macro exchanger_method_do_exchange - # @!macro exchanger_method_exchange - - # @!method exchange!(value, timeout = nil) - # @!macro exchanger_method_do_exchange - # @!macro exchanger_method_exchange_bang - - # @!method try_exchange(value, timeout = nil) - # @!macro exchanger_method_do_exchange - # @!macro exchanger_method_try_exchange - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/abstract_executor_service.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -require 'concurrent/errors' -require 'concurrent/executor/executor_service' -require 'concurrent/synchronization' -require 'concurrent/utility/at_exit' - -module Concurrent - - # @!macro abstract_executor_service_public_api - # @!visibility private - class AbstractExecutorService < Synchronization::LockableObject - include ExecutorService - - # The set of possible fallback policies that may be set at thread pool creation. - FALLBACK_POLICIES = [:abort, :discard, :caller_runs].freeze - - # @!macro executor_service_attr_reader_fallback_policy - attr_reader :fallback_policy - - # Create a new thread pool. - def initialize(*args, &block) - super(&nil) - synchronize { ns_initialize(*args, &block) } - end - - # @!macro executor_service_method_shutdown - def shutdown - raise NotImplementedError - end - - # @!macro executor_service_method_kill - def kill - raise NotImplementedError - end - - # @!macro executor_service_method_wait_for_termination - def wait_for_termination(timeout = nil) - raise NotImplementedError - end - - # @!macro executor_service_method_running_question - def running? - synchronize { ns_running? } - end - - # @!macro executor_service_method_shuttingdown_question - def shuttingdown? - synchronize { ns_shuttingdown? } - end - - # @!macro executor_service_method_shutdown_question - def shutdown? - synchronize { ns_shutdown? } - end - - # @!macro executor_service_method_auto_terminate_question - def auto_terminate? - synchronize { ns_auto_terminate? } - end - - # @!macro executor_service_method_auto_terminate_setter - def auto_terminate=(value) - synchronize { self.ns_auto_terminate = value } - end - - private - - # Handler which executes the `fallback_policy` once the queue size - # reaches `max_queue`. - # - # @param [Array] args the arguments to the task which is being handled. - # - # @!visibility private - def handle_fallback(*args) - case fallback_policy - when :abort - raise RejectedExecutionError - when :discard - false - when :caller_runs - begin - yield(*args) - rescue => ex - # let it fail - log DEBUG, ex - end - true - else - fail "Unknown fallback policy #{fallback_policy}" - end - end - - def ns_execute(*args, &task) - raise NotImplementedError - end - - # @!macro executor_service_method_ns_shutdown_execution - # - # Callback method called when an orderly shutdown has completed. - # The default behavior is to signal all waiting threads. - def ns_shutdown_execution - # do nothing - end - - # @!macro executor_service_method_ns_kill_execution - # - # Callback method called when the executor has been killed. - # The default behavior is to do nothing. - def ns_kill_execution - # do nothing - end - - def ns_auto_terminate? - !!@auto_terminate - end - - def ns_auto_terminate=(value) - case value - when true - AtExit.add(self) { terminate_at_exit } - @auto_terminate = true - when false - AtExit.delete(self) - @auto_terminate = false - else - raise ArgumentError - end - end - - def terminate_at_exit - kill # TODO be gentle first - wait_for_termination(10) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/cached_thread_pool.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -require 'concurrent/utility/engine' -require 'concurrent/executor/thread_pool_executor' - -module Concurrent - - # A thread pool that dynamically grows and shrinks to fit the current workload. - # New threads are created as needed, existing threads are reused, and threads - # that remain idle for too long are killed and removed from the pool. These - # pools are particularly suited to applications that perform a high volume of - # short-lived tasks. - # - # On creation a `CachedThreadPool` has zero running threads. New threads are - # created on the pool as new operations are `#post`. The size of the pool - # will grow until `#max_length` threads are in the pool or until the number - # of threads exceeds the number of running and pending operations. When a new - # operation is post to the pool the first available idle thread will be tasked - # with the new operation. - # - # Should a thread crash for any reason the thread will immediately be removed - # from the pool. Similarly, threads which remain idle for an extended period - # of time will be killed and reclaimed. Thus these thread pools are very - # efficient at reclaiming unused resources. - # - # The API and behavior of this class are based on Java's `CachedThreadPool` - # - # @!macro thread_pool_options - class CachedThreadPool < ThreadPoolExecutor - - # @!macro cached_thread_pool_method_initialize - # - # Create a new thread pool. - # - # @param [Hash] opts the options defining pool behavior. - # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy - # - # @raise [ArgumentError] if `fallback_policy` is not a known policy - # - # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool-- - def initialize(opts = {}) - defaults = { idletime: DEFAULT_THREAD_IDLETIMEOUT } - overrides = { min_threads: 0, - max_threads: DEFAULT_MAX_POOL_SIZE, - max_queue: DEFAULT_MAX_QUEUE_SIZE } - super(defaults.merge(opts).merge(overrides)) - end - - private - - # @!macro cached_thread_pool_method_initialize - # @!visibility private - def ns_initialize(opts) - super(opts) - if Concurrent.on_jruby? - @max_queue = 0 - @executor = java.util.concurrent.Executors.newCachedThreadPool - @executor.setRejectedExecutionHandler(FALLBACK_POLICY_CLASSES[@fallback_policy].new) - @executor.setKeepAliveTime(opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT), java.util.concurrent.TimeUnit::SECONDS) - self.auto_terminate = opts.fetch(:auto_terminate, true) - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/executor_service.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -require 'concurrent/concern/logging' - -module Concurrent - - ################################################################### - - # @!macro executor_service_method_post - # - # Submit a task to the executor for asynchronous processing. - # - # @param [Array] args zero or more arguments to be passed to the task - # - # @yield the asynchronous task to perform - # - # @return [Boolean] `true` if the task is queued, `false` if the executor - # is not running - # - # @raise [ArgumentError] if no task is given - - # @!macro executor_service_method_left_shift - # - # Submit a task to the executor for asynchronous processing. - # - # @param [Proc] task the asynchronous task to perform - # - # @return [self] returns itself - - # @!macro executor_service_method_can_overflow_question - # - # Does the task queue have a maximum size? - # - # @return [Boolean] True if the task queue has a maximum size else false. - - # @!macro executor_service_method_serialized_question - # - # Does this executor guarantee serialization of its operations? - # - # @return [Boolean] True if the executor guarantees that all operations - # will be post in the order they are received and no two operations may - # occur simultaneously. Else false. - - ################################################################### - - # @!macro executor_service_public_api - # - # @!method post(*args, &task) - # @!macro executor_service_method_post - # - # @!method <<(task) - # @!macro executor_service_method_left_shift - # - # @!method can_overflow? - # @!macro executor_service_method_can_overflow_question - # - # @!method serialized? - # @!macro executor_service_method_serialized_question - - ################################################################### - - # @!macro executor_service_attr_reader_fallback_policy - # @return [Symbol] The fallback policy in effect. Either `:abort`, `:discard`, or `:caller_runs`. - - # @!macro executor_service_method_shutdown - # - # Begin an orderly shutdown. Tasks already in the queue will be executed, - # but no new tasks will be accepted. Has no additional effect if the - # thread pool is not running. - - # @!macro executor_service_method_kill - # - # Begin an immediate shutdown. In-progress tasks will be allowed to - # complete but enqueued tasks will be dismissed and no new tasks - # will be accepted. Has no additional effect if the thread pool is - # not running. - - # @!macro executor_service_method_wait_for_termination - # - # Block until executor shutdown is complete or until `timeout` seconds have - # passed. - # - # @note Does not initiate shutdown or termination. Either `shutdown` or `kill` - # must be called before this method (or on another thread). - # - # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete - # - # @return [Boolean] `true` if shutdown complete or false on `timeout` - - # @!macro executor_service_method_running_question - # - # Is the executor running? - # - # @return [Boolean] `true` when running, `false` when shutting down or shutdown - - # @!macro executor_service_method_shuttingdown_question - # - # Is the executor shuttingdown? - # - # @return [Boolean] `true` when not running and not shutdown, else `false` - - # @!macro executor_service_method_shutdown_question - # - # Is the executor shutdown? - # - # @return [Boolean] `true` when shutdown, `false` when shutting down or running - - # @!macro executor_service_method_auto_terminate_question - # - # Is the executor auto-terminate when the application exits? - # - # @return [Boolean] `true` when auto-termination is enabled else `false`. - - # @!macro executor_service_method_auto_terminate_setter - # - # Set the auto-terminate behavior for this executor. - # - # @param [Boolean] value The new auto-terminate value to set for this executor. - # - # @return [Boolean] `true` when auto-termination is enabled else `false`. - - ################################################################### - - # @!macro abstract_executor_service_public_api - # - # @!macro executor_service_public_api - # - # @!attribute [r] fallback_policy - # @!macro executor_service_attr_reader_fallback_policy - # - # @!method shutdown - # @!macro executor_service_method_shutdown - # - # @!method kill - # @!macro executor_service_method_kill - # - # @!method wait_for_termination(timeout = nil) - # @!macro executor_service_method_wait_for_termination - # - # @!method running? - # @!macro executor_service_method_running_question - # - # @!method shuttingdown? - # @!macro executor_service_method_shuttingdown_question - # - # @!method shutdown? - # @!macro executor_service_method_shutdown_question - # - # @!method auto_terminate? - # @!macro executor_service_method_auto_terminate_question - # - # @!method auto_terminate=(value) - # @!macro executor_service_method_auto_terminate_setter - - ################################################################### - - # @!macro executor_service_public_api - # @!visibility private - module ExecutorService - include Concern::Logging - - # @!macro executor_service_method_post - def post(*args, &task) - raise NotImplementedError - end - - # @!macro executor_service_method_left_shift - def <<(task) - post(&task) - self - end - - # @!macro executor_service_method_can_overflow_question - # - # @note Always returns `false` - def can_overflow? - false - end - - # @!macro executor_service_method_serialized_question - # - # @note Always returns `false` - def serialized? - false - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/fixed_thread_pool.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,206 +0,0 @@ -require 'concurrent/utility/engine' -require 'concurrent/executor/thread_pool_executor' - -module Concurrent - - # @!macro thread_pool_executor_constant_default_max_pool_size - # Default maximum number of threads that will be created in the pool. - - # @!macro thread_pool_executor_constant_default_min_pool_size - # Default minimum number of threads that will be retained in the pool. - - # @!macro thread_pool_executor_constant_default_max_queue_size - # Default maximum number of tasks that may be added to the task queue. - - # @!macro thread_pool_executor_constant_default_thread_timeout - # Default maximum number of seconds a thread in the pool may remain idle - # before being reclaimed. - - # @!macro thread_pool_executor_attr_reader_max_length - # The maximum number of threads that may be created in the pool. - # @return [Integer] The maximum number of threads that may be created in the pool. - - # @!macro thread_pool_executor_attr_reader_min_length - # The minimum number of threads that may be retained in the pool. - # @return [Integer] The minimum number of threads that may be retained in the pool. - - # @!macro thread_pool_executor_attr_reader_largest_length - # The largest number of threads that have been created in the pool since construction. - # @return [Integer] The largest number of threads that have been created in the pool since construction. - - # @!macro thread_pool_executor_attr_reader_scheduled_task_count - # The number of tasks that have been scheduled for execution on the pool since construction. - # @return [Integer] The number of tasks that have been scheduled for execution on the pool since construction. - - # @!macro thread_pool_executor_attr_reader_completed_task_count - # The number of tasks that have been completed by the pool since construction. - # @return [Integer] The number of tasks that have been completed by the pool since construction. - - # @!macro thread_pool_executor_attr_reader_idletime - # The number of seconds that a thread may be idle before being reclaimed. - # @return [Integer] The number of seconds that a thread may be idle before being reclaimed. - - # @!macro thread_pool_executor_attr_reader_max_queue - # The maximum number of tasks that may be waiting in the work queue at any one time. - # When the queue size reaches `max_queue` subsequent tasks will be rejected in - # accordance with the configured `fallback_policy`. - # - # @return [Integer] The maximum number of tasks that may be waiting in the work queue at any one time. - # When the queue size reaches `max_queue` subsequent tasks will be rejected in - # accordance with the configured `fallback_policy`. - - # @!macro thread_pool_executor_attr_reader_length - # The number of threads currently in the pool. - # @return [Integer] The number of threads currently in the pool. - - # @!macro thread_pool_executor_attr_reader_queue_length - # The number of tasks in the queue awaiting execution. - # @return [Integer] The number of tasks in the queue awaiting execution. - - # @!macro thread_pool_executor_attr_reader_remaining_capacity - # Number of tasks that may be enqueued before reaching `max_queue` and rejecting - # new tasks. A value of -1 indicates that the queue may grow without bound. - # - # @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting - # new tasks. A value of -1 indicates that the queue may grow without bound. - - - - - - # @!macro thread_pool_executor_public_api - # - # @!macro abstract_executor_service_public_api - # - # @!attribute [r] max_length - # @!macro thread_pool_executor_attr_reader_max_length - # - # @!attribute [r] min_length - # @!macro thread_pool_executor_attr_reader_min_length - # - # @!attribute [r] largest_length - # @!macro thread_pool_executor_attr_reader_largest_length - # - # @!attribute [r] scheduled_task_count - # @!macro thread_pool_executor_attr_reader_scheduled_task_count - # - # @!attribute [r] completed_task_count - # @!macro thread_pool_executor_attr_reader_completed_task_count - # - # @!attribute [r] idletime - # @!macro thread_pool_executor_attr_reader_idletime - # - # @!attribute [r] max_queue - # @!macro thread_pool_executor_attr_reader_max_queue - # - # @!attribute [r] length - # @!macro thread_pool_executor_attr_reader_length - # - # @!attribute [r] queue_length - # @!macro thread_pool_executor_attr_reader_queue_length - # - # @!attribute [r] remaining_capacity - # @!macro thread_pool_executor_attr_reader_remaining_capacity - # - # @!method can_overflow? - # @!macro executor_service_method_can_overflow_question - - - - - # @!macro thread_pool_options - # - # **Thread Pool Options** - # - # Thread pools support several configuration options: - # - # * `idletime`: The number of seconds that a thread may be idle before being reclaimed. - # * `max_queue`: The maximum number of tasks that may be waiting in the work queue at - # any one time. When the queue size reaches `max_queue` and no new threads can be created, - # subsequent tasks will be rejected in accordance with the configured `fallback_policy`. - # * `auto_terminate`: When true (default) an `at_exit` handler will be registered which - # will stop the thread pool when the application exits. See below for more information - # on shutting down thread pools. - # * `fallback_policy`: The policy defining how rejected tasks are handled. - # - # Three fallback policies are supported: - # - # * `:abort`: Raise a `RejectedExecutionError` exception and discard the task. - # * `:discard`: Discard the task and return false. - # * `:caller_runs`: Execute the task on the calling thread. - # - # **Shutting Down Thread Pools** - # - # Killing a thread pool while tasks are still being processed, either by calling - # the `#kill` method or at application exit, will have unpredictable results. There - # is no way for the thread pool to know what resources are being used by the - # in-progress tasks. When those tasks are killed the impact on those resources - # cannot be predicted. The *best* practice is to explicitly shutdown all thread - # pools using the provided methods: - # - # * Call `#shutdown` to initiate an orderly termination of all in-progress tasks - # * Call `#wait_for_termination` with an appropriate timeout interval an allow - # the orderly shutdown to complete - # * Call `#kill` *only when* the thread pool fails to shutdown in the allotted time - # - # On some runtime platforms (most notably the JVM) the application will not - # exit until all thread pools have been shutdown. To prevent applications from - # "hanging" on exit all thread pools include an `at_exit` handler that will - # stop the thread pool when the application exits. This handler uses a brute - # force method to stop the pool and makes no guarantees regarding resources being - # used by any tasks still running. Registration of this `at_exit` handler can be - # prevented by setting the thread pool's constructor `:auto_terminate` option to - # `false` when the thread pool is created. All thread pools support this option. - # - # ```ruby - # pool1 = Concurrent::FixedThreadPool.new(5) # an `at_exit` handler will be registered - # pool2 = Concurrent::FixedThreadPool.new(5, auto_terminate: false) # prevent `at_exit` handler registration - # ``` - # - # @note Failure to properly shutdown a thread pool can lead to unpredictable results. - # Please read *Shutting Down Thread Pools* for more information. - # - # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html Java Tutorials: Thread Pools - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html Java Executors class - # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html Java ExecutorService interface - # @see http://ruby-doc.org//core-2.2.0/Kernel.html#method-i-at_exit Kernel#at_exit - - - - - - # @!macro fixed_thread_pool - # - # A thread pool that reuses a fixed number of threads operating off an unbounded queue. - # At any point, at most `num_threads` will be active processing tasks. When all threads are busy new - # tasks `#post` to the thread pool are enqueued until a thread becomes available. - # Should a thread crash for any reason the thread will immediately be removed - # from the pool and replaced. - # - # The API and behavior of this class are based on Java's `FixedThreadPool` - # - # @!macro thread_pool_options - class FixedThreadPool < ThreadPoolExecutor - - # @!macro fixed_thread_pool_method_initialize - # - # Create a new thread pool. - # - # @param [Integer] num_threads the number of threads to allocate - # @param [Hash] opts the options defining pool behavior. - # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy - # - # @raise [ArgumentError] if `num_threads` is less than or equal to zero - # @raise [ArgumentError] if `fallback_policy` is not a known policy - # - # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int- - def initialize(num_threads, opts = {}) - raise ArgumentError.new('number of threads must be greater than zero') if num_threads.to_i < 1 - defaults = { max_queue: DEFAULT_MAX_QUEUE_SIZE, - idletime: DEFAULT_THREAD_IDLETIMEOUT } - overrides = { min_threads: num_threads, - max_threads: num_threads } - super(defaults.merge(opts).merge(overrides)) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/immediate_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -require 'concurrent/atomic/event' -require 'concurrent/executor/abstract_executor_service' -require 'concurrent/executor/serial_executor_service' - -module Concurrent - - # An executor service which runs all operations on the current thread, - # blocking as necessary. Operations are performed in the order they are - # received and no two operations can be performed simultaneously. - # - # This executor service exists mainly for testing an debugging. When used - # it immediately runs every `#post` operation on the current thread, blocking - # that thread until the operation is complete. This can be very beneficial - # during testing because it makes all operations deterministic. - # - # @note Intended for use primarily in testing and debugging. - class ImmediateExecutor < AbstractExecutorService - include SerialExecutorService - - # Creates a new executor - def initialize - @stopped = Concurrent::Event.new - end - - # @!macro executor_service_method_post - def post(*args, &task) - raise ArgumentError.new('no block given') unless block_given? - return false unless running? - task.call(*args) - true - end - - # @!macro executor_service_method_left_shift - def <<(task) - post(&task) - self - end - - # @!macro executor_service_method_running_question - def running? - ! shutdown? - end - - # @!macro executor_service_method_shuttingdown_question - def shuttingdown? - false - end - - # @!macro executor_service_method_shutdown_question - def shutdown? - @stopped.set? - end - - # @!macro executor_service_method_shutdown - def shutdown - @stopped.set - true - end - alias_method :kill, :shutdown - - # @!macro executor_service_method_wait_for_termination - def wait_for_termination(timeout = nil) - @stopped.wait(timeout) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/indirect_immediate_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -require 'concurrent/executor/immediate_executor' -require 'concurrent/executor/simple_executor_service' - -module Concurrent - # An executor service which runs all operations on a new thread, blocking - # until it completes. Operations are performed in the order they are received - # and no two operations can be performed simultaneously. - # - # This executor service exists mainly for testing an debugging. When used it - # immediately runs every `#post` operation on a new thread, blocking the - # current thread until the operation is complete. This is similar to how the - # ImmediateExecutor works, but the operation has the full stack of the new - # thread at its disposal. This can be helpful when the operations will spawn - # more operations on the same executor and so on - such a situation might - # overflow the single stack in case of an ImmediateExecutor, which is - # inconsistent with how it would behave for a threaded executor. - # - # @note Intended for use primarily in testing and debugging. - class IndirectImmediateExecutor < ImmediateExecutor - # Creates a new executor - def initialize - super - @internal_executor = SimpleExecutorService.new - end - - # @!macro executor_service_method_post - def post(*args, &task) - raise ArgumentError.new("no block given") unless block_given? - return false unless running? - - event = Concurrent::Event.new - @internal_executor.post do - begin - task.call(*args) - ensure - event.set - end - end - event.wait - - true - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_executor_service.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -if Concurrent.on_jruby? - - require 'concurrent/errors' - require 'concurrent/utility/engine' - require 'concurrent/executor/abstract_executor_service' - - module Concurrent - - # @!macro abstract_executor_service_public_api - # @!visibility private - class JavaExecutorService < AbstractExecutorService - java_import 'java.lang.Runnable' - - FALLBACK_POLICY_CLASSES = { - abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy, - discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy, - caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy - }.freeze - private_constant :FALLBACK_POLICY_CLASSES - - def initialize(*args, &block) - super - end - - def post(*args, &task) - raise ArgumentError.new('no block given') unless block_given? - return handle_fallback(*args, &task) unless running? - @executor.submit Job.new(args, task) - true - rescue Java::JavaUtilConcurrent::RejectedExecutionException - raise RejectedExecutionError - end - - def wait_for_termination(timeout = nil) - if timeout.nil? - ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok - true - else - @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS) - end - end - - def shutdown - synchronize do - self.ns_auto_terminate = false - @executor.shutdown - nil - end - end - - def kill - synchronize do - self.ns_auto_terminate = false - @executor.shutdownNow - nil - end - end - - private - - def ns_running? - !(ns_shuttingdown? || ns_shutdown?) - end - - def ns_shuttingdown? - if @executor.respond_to? :isTerminating - @executor.isTerminating - else - false - end - end - - def ns_shutdown? - @executor.isShutdown || @executor.isTerminated - end - - class Job - include Runnable - def initialize(args, block) - @args = args - @block = block - end - - def run - @block.call(*@args) - end - end - private_constant :Job - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_single_thread_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -if Concurrent.on_jruby? - - require 'concurrent/executor/java_executor_service' - require 'concurrent/executor/serial_executor_service' - - module Concurrent - - # @!macro single_thread_executor - # @!macro abstract_executor_service_public_api - # @!visibility private - class JavaSingleThreadExecutor < JavaExecutorService - include SerialExecutorService - - # @!macro single_thread_executor_method_initialize - def initialize(opts = {}) - super(opts) - end - - private - - def ns_initialize(opts) - @executor = java.util.concurrent.Executors.newSingleThreadExecutor - @fallback_policy = opts.fetch(:fallback_policy, :discard) - raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.keys.include?(@fallback_policy) - self.auto_terminate = opts.fetch(:auto_terminate, true) - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/java_thread_pool_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -if Concurrent.on_jruby? - - require 'concurrent/executor/java_executor_service' - - module Concurrent - - # @!macro thread_pool_executor - # @!macro thread_pool_options - # @!visibility private - class JavaThreadPoolExecutor < JavaExecutorService - - # @!macro thread_pool_executor_constant_default_max_pool_size - DEFAULT_MAX_POOL_SIZE = java.lang.Integer::MAX_VALUE # 2147483647 - - # @!macro thread_pool_executor_constant_default_min_pool_size - DEFAULT_MIN_POOL_SIZE = 0 - - # @!macro thread_pool_executor_constant_default_max_queue_size - DEFAULT_MAX_QUEUE_SIZE = 0 - - # @!macro thread_pool_executor_constant_default_thread_timeout - DEFAULT_THREAD_IDLETIMEOUT = 60 - - # @!macro thread_pool_executor_attr_reader_max_length - attr_reader :max_length - - # @!macro thread_pool_executor_attr_reader_max_queue - attr_reader :max_queue - - # @!macro thread_pool_executor_method_initialize - def initialize(opts = {}) - super(opts) - end - - # @!macro executor_service_method_can_overflow_question - def can_overflow? - @max_queue != 0 - end - - # @!macro thread_pool_executor_attr_reader_min_length - def min_length - @executor.getCorePoolSize - end - - # @!macro thread_pool_executor_attr_reader_max_length - def max_length - @executor.getMaximumPoolSize - end - - # @!macro thread_pool_executor_attr_reader_length - def length - @executor.getPoolSize - end - - # @!macro thread_pool_executor_attr_reader_largest_length - def largest_length - @executor.getLargestPoolSize - end - - # @!macro thread_pool_executor_attr_reader_scheduled_task_count - def scheduled_task_count - @executor.getTaskCount - end - - # @!macro thread_pool_executor_attr_reader_completed_task_count - def completed_task_count - @executor.getCompletedTaskCount - end - - # @!macro thread_pool_executor_attr_reader_idletime - def idletime - @executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS) - end - - # @!macro thread_pool_executor_attr_reader_queue_length - def queue_length - @executor.getQueue.size - end - - # @!macro thread_pool_executor_attr_reader_remaining_capacity - def remaining_capacity - @max_queue == 0 ? -1 : @executor.getQueue.remainingCapacity - end - - # @!macro executor_service_method_running_question - def running? - super && !@executor.isTerminating - end - - private - - def ns_initialize(opts) - min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i - max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i - idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i - @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i - @fallback_policy = opts.fetch(:fallback_policy, :abort) - - raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE - raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE - raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE - raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length - raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy) - - if @max_queue == 0 - queue = java.util.concurrent.LinkedBlockingQueue.new - else - queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue) - end - - @executor = java.util.concurrent.ThreadPoolExecutor.new( - min_length, - max_length, - idletime, - java.util.concurrent.TimeUnit::SECONDS, - queue, - FALLBACK_POLICY_CLASSES[@fallback_policy].new) - - self.auto_terminate = opts.fetch(:auto_terminate, true) - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_executor_service.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -require 'concurrent/executor/abstract_executor_service' -require 'concurrent/atomic/event' - -module Concurrent - - # @!macro abstract_executor_service_public_api - # @!visibility private - class RubyExecutorService < AbstractExecutorService - safe_initialization! - - def initialize(*args, &block) - super - @StopEvent = Event.new - @StoppedEvent = Event.new - end - - def post(*args, &task) - raise ArgumentError.new('no block given') unless block_given? - synchronize do - # If the executor is shut down, reject this task - return handle_fallback(*args, &task) unless running? - ns_execute(*args, &task) - true - end - end - - def shutdown - synchronize do - break unless running? - self.ns_auto_terminate = false - stop_event.set - ns_shutdown_execution - end - true - end - - def kill - synchronize do - break if shutdown? - self.ns_auto_terminate = false - stop_event.set - ns_kill_execution - stopped_event.set - end - true - end - - def wait_for_termination(timeout = nil) - stopped_event.wait(timeout) - end - - private - - def stop_event - @StopEvent - end - - def stopped_event - @StoppedEvent - end - - def ns_shutdown_execution - stopped_event.set - end - - def ns_running? - !stop_event.set? - end - - def ns_shuttingdown? - !(ns_running? || ns_shutdown?) - end - - def ns_shutdown? - stopped_event.set? - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_single_thread_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -require 'concurrent/executor/ruby_thread_pool_executor' - -module Concurrent - - # @!macro single_thread_executor - # @!macro abstract_executor_service_public_api - # @!visibility private - class RubySingleThreadExecutor < RubyThreadPoolExecutor - - # @!macro single_thread_executor_method_initialize - def initialize(opts = {}) - super( - min_threads: 1, - max_threads: 1, - max_queue: 0, - idletime: DEFAULT_THREAD_IDLETIMEOUT, - fallback_policy: opts.fetch(:fallback_policy, :discard), - auto_terminate: opts.fetch(:auto_terminate, true) - ) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/ruby_thread_pool_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,362 +0,0 @@ -require 'thread' -require 'concurrent/atomic/event' -require 'concurrent/concern/logging' -require 'concurrent/executor/ruby_executor_service' -require 'concurrent/utility/monotonic_time' - -module Concurrent - - # @!macro thread_pool_executor - # @!macro thread_pool_options - # @!visibility private - class RubyThreadPoolExecutor < RubyExecutorService - - # @!macro thread_pool_executor_constant_default_max_pool_size - DEFAULT_MAX_POOL_SIZE = 2_147_483_647 # java.lang.Integer::MAX_VALUE - - # @!macro thread_pool_executor_constant_default_min_pool_size - DEFAULT_MIN_POOL_SIZE = 0 - - # @!macro thread_pool_executor_constant_default_max_queue_size - DEFAULT_MAX_QUEUE_SIZE = 0 - - # @!macro thread_pool_executor_constant_default_thread_timeout - DEFAULT_THREAD_IDLETIMEOUT = 60 - - # @!macro thread_pool_executor_attr_reader_max_length - attr_reader :max_length - - # @!macro thread_pool_executor_attr_reader_min_length - attr_reader :min_length - - # @!macro thread_pool_executor_attr_reader_idletime - attr_reader :idletime - - # @!macro thread_pool_executor_attr_reader_max_queue - attr_reader :max_queue - - # @!macro thread_pool_executor_method_initialize - def initialize(opts = {}) - super(opts) - end - - # @!macro thread_pool_executor_attr_reader_largest_length - def largest_length - synchronize { @largest_length } - end - - # @!macro thread_pool_executor_attr_reader_scheduled_task_count - def scheduled_task_count - synchronize { @scheduled_task_count } - end - - # @!macro thread_pool_executor_attr_reader_completed_task_count - def completed_task_count - synchronize { @completed_task_count } - end - - # @!macro executor_service_method_can_overflow_question - def can_overflow? - synchronize { ns_limited_queue? } - end - - # @!macro thread_pool_executor_attr_reader_length - def length - synchronize { @pool.length } - end - - # @!macro thread_pool_executor_attr_reader_queue_length - def queue_length - synchronize { @queue.length } - end - - # @!macro thread_pool_executor_attr_reader_remaining_capacity - def remaining_capacity - synchronize do - if ns_limited_queue? - @max_queue - @queue.length - else - -1 - end - end - end - - # @!visibility private - def remove_busy_worker(worker) - synchronize { ns_remove_busy_worker worker } - end - - # @!visibility private - def ready_worker(worker) - synchronize { ns_ready_worker worker } - end - - # @!visibility private - def worker_not_old_enough(worker) - synchronize { ns_worker_not_old_enough worker } - end - - # @!visibility private - def worker_died(worker) - synchronize { ns_worker_died worker } - end - - # @!visibility private - def worker_task_completed - synchronize { @completed_task_count += 1 } - end - - private - - # @!visibility private - def ns_initialize(opts) - @min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i - @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i - @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i - @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i - @fallback_policy = opts.fetch(:fallback_policy, :abort) - raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy) - - raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE - raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE - raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE - raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length - - self.auto_terminate = opts.fetch(:auto_terminate, true) - - @pool = [] # all workers - @ready = [] # used as a stash (most idle worker is at the start) - @queue = [] # used as queue - # @ready or @queue is empty at all times - @scheduled_task_count = 0 - @completed_task_count = 0 - @largest_length = 0 - @ruby_pid = $$ # detects if Ruby has forked - - @gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented - @next_gc_time = Concurrent.monotonic_time + @gc_interval - end - - # @!visibility private - def ns_limited_queue? - @max_queue != 0 - end - - # @!visibility private - def ns_execute(*args, &task) - ns_reset_if_forked - - if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task) - @scheduled_task_count += 1 - else - handle_fallback(*args, &task) - end - - ns_prune_pool if @next_gc_time < Concurrent.monotonic_time - end - - # @!visibility private - def ns_shutdown_execution - ns_reset_if_forked - - if @pool.empty? - # nothing to do - stopped_event.set - end - - if @queue.empty? - # no more tasks will be accepted, just stop all workers - @pool.each(&:stop) - end - end - - # @!visibility private - def ns_kill_execution - # TODO log out unprocessed tasks in queue - # TODO try to shutdown first? - @pool.each(&:kill) - @pool.clear - @ready.clear - end - - # tries to assign task to a worker, tries to get one from @ready or to create new one - # @return [true, false] if task is assigned to a worker - # - # @!visibility private - def ns_assign_worker(*args, &task) - # keep growing if the pool is not at the minimum yet - worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker - if worker - worker << [task, args] - true - else - false - end - rescue ThreadError - # Raised when the operating system refuses to create the new thread - return false - end - - # tries to enqueue task - # @return [true, false] if enqueued - # - # @!visibility private - def ns_enqueue(*args, &task) - if !ns_limited_queue? || @queue.size < @max_queue - @queue << [task, args] - true - else - false - end - end - - # @!visibility private - def ns_worker_died(worker) - ns_remove_busy_worker worker - replacement_worker = ns_add_busy_worker - ns_ready_worker replacement_worker, false if replacement_worker - end - - # creates new worker which has to receive work to do after it's added - # @return [nil, Worker] nil of max capacity is reached - # - # @!visibility private - def ns_add_busy_worker - return if @pool.size >= @max_length - - @pool << (worker = Worker.new(self)) - @largest_length = @pool.length if @pool.length > @largest_length - worker - end - - # handle ready worker, giving it new job or assigning back to @ready - # - # @!visibility private - def ns_ready_worker(worker, success = true) - task_and_args = @queue.shift - if task_and_args - worker << task_and_args - else - # stop workers when !running?, do not return them to @ready - if running? - @ready.push(worker) - else - worker.stop - end - end - end - - # returns back worker to @ready which was not idle for enough time - # - # @!visibility private - def ns_worker_not_old_enough(worker) - # let's put workers coming from idle_test back to the start (as the oldest worker) - @ready.unshift(worker) - true - end - - # removes a worker which is not in not tracked in @ready - # - # @!visibility private - def ns_remove_busy_worker(worker) - @pool.delete(worker) - stopped_event.set if @pool.empty? && !running? - true - end - - # try oldest worker if it is idle for enough time, it's returned back at the start - # - # @!visibility private - def ns_prune_pool - return if @pool.size <= @min_length - - last_used = @ready.shift - last_used << :idle_test if last_used - - @next_gc_time = Concurrent.monotonic_time + @gc_interval - end - - def ns_reset_if_forked - if $$ != @ruby_pid - @queue.clear - @ready.clear - @pool.clear - @scheduled_task_count = 0 - @completed_task_count = 0 - @largest_length = 0 - @ruby_pid = $$ - end - end - - # @!visibility private - class Worker - include Concern::Logging - - def initialize(pool) - # instance variables accessed only under pool's lock so no need to sync here again - @queue = Queue.new - @pool = pool - @thread = create_worker @queue, pool, pool.idletime - end - - def <<(message) - @queue << message - end - - def stop - @queue << :stop - end - - def kill - @thread.kill - end - - private - - def create_worker(queue, pool, idletime) - Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime| - last_message = Concurrent.monotonic_time - catch(:stop) do - loop do - - case message = my_queue.pop - when :idle_test - if (Concurrent.monotonic_time - last_message) > my_idletime - my_pool.remove_busy_worker(self) - throw :stop - else - my_pool.worker_not_old_enough(self) - end - - when :stop - my_pool.remove_busy_worker(self) - throw :stop - - else - task, args = message - run_task my_pool, task, args - last_message = Concurrent.monotonic_time - - my_pool.ready_worker(self) - end - end - end - end - end - - def run_task(pool, task, args) - task.call(*args) - pool.worker_task_completed - rescue => ex - # let it fail - log DEBUG, ex - rescue Exception => ex - log ERROR, ex - pool.worker_died(self) - throw :stop - end - end - - private_constant :Worker - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/safe_task_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -require 'concurrent/synchronization' - -module Concurrent - - # A simple utility class that executes a callable and returns and array of three elements: - # success - indicating if the callable has been executed without errors - # value - filled by the callable result if it has been executed without errors, nil otherwise - # reason - the error risen by the callable if it has been executed with errors, nil otherwise - class SafeTaskExecutor < Synchronization::LockableObject - - def initialize(task, opts = {}) - @task = task - @exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError - super() # ensures visibility - end - - # @return [Array] - def execute(*args) - synchronize do - success = false - value = reason = nil - - begin - value = @task.call(*args) - success = true - rescue @exception_class => ex - reason = ex - success = false - end - - [success, value, reason] - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serial_executor_service.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -require 'concurrent/executor/executor_service' - -module Concurrent - - # Indicates that the including `ExecutorService` guarantees - # that all operations will occur in the order they are post and that no - # two operations may occur simultaneously. This module provides no - # functionality and provides no guarantees. That is the responsibility - # of the including class. This module exists solely to allow the including - # object to be interrogated for its serialization status. - # - # @example - # class Foo - # include Concurrent::SerialExecutor - # end - # - # foo = Foo.new - # - # foo.is_a? Concurrent::ExecutorService #=> true - # foo.is_a? Concurrent::SerialExecutor #=> true - # foo.serialized? #=> true - # - # @!visibility private - module SerialExecutorService - include ExecutorService - - # @!macro executor_service_method_serialized_question - # - # @note Always returns `true` - def serialized? - true - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -require 'concurrent/errors' -require 'concurrent/concern/logging' -require 'concurrent/synchronization' - -module Concurrent - - # Ensures passed jobs in a serialized order never running at the same time. - class SerializedExecution < Synchronization::LockableObject - include Concern::Logging - - def initialize() - super() - synchronize { ns_initialize } - end - - Job = Struct.new(:executor, :args, :block) do - def call - block.call(*args) - end - end - - # Submit a task to the executor for asynchronous processing. - # - # @param [Executor] executor to be used for this job - # - # @param [Array] args zero or more arguments to be passed to the task - # - # @yield the asynchronous task to perform - # - # @return [Boolean] `true` if the task is queued, `false` if the executor - # is not running - # - # @raise [ArgumentError] if no task is given - def post(executor, *args, &task) - posts [[executor, args, task]] - true - end - - # As {#post} but allows to submit multiple tasks at once, it's guaranteed that they will not - # be interleaved by other tasks. - # - # @param [Array, Proc)>] posts array of triplets where - # first is a {ExecutorService}, second is array of args for task, third is a task (Proc) - def posts(posts) - # if can_overflow? - # raise ArgumentError, 'SerializedExecution does not support thread-pools which can overflow' - # end - - return nil if posts.empty? - - jobs = posts.map { |executor, args, task| Job.new executor, args, task } - - job_to_post = synchronize do - if @being_executed - @stash.push(*jobs) - nil - else - @being_executed = true - @stash.push(*jobs[1..-1]) - jobs.first - end - end - - call_job job_to_post if job_to_post - true - end - - private - - def ns_initialize - @being_executed = false - @stash = [] - end - - def call_job(job) - did_it_run = begin - job.executor.post { work(job) } - true - rescue RejectedExecutionError => ex - false - end - - # TODO not the best idea to run it myself - unless did_it_run - begin - work job - rescue => ex - # let it fail - log DEBUG, ex - end - end - end - - # ensures next job is executed if any is stashed - def work(job) - job.call - ensure - synchronize do - job = @stash.shift || (@being_executed = false) - end - - # TODO maybe be able to tell caching pool to just enqueue this job, because the current one end at the end - # of this block - call_job job if job - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/serialized_execution_delegator.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -require 'delegate' -require 'concurrent/executor/serial_executor_service' -require 'concurrent/executor/serialized_execution' - -module Concurrent - - # A wrapper/delegator for any `ExecutorService` that - # guarantees serialized execution of tasks. - # - # @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html) - # @see Concurrent::SerializedExecution - class SerializedExecutionDelegator < SimpleDelegator - include SerialExecutorService - - def initialize(executor) - @executor = executor - @serializer = SerializedExecution.new - super(executor) - end - - # @!macro executor_service_method_post - def post(*args, &task) - raise ArgumentError.new('no block given') unless block_given? - return false unless running? - @serializer.post(@executor, *args, &task) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/simple_executor_service.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -require 'concurrent/atomics' -require 'concurrent/executor/executor_service' - -module Concurrent - - # An executor service in which every operation spawns a new, - # independently operating thread. - # - # This is perhaps the most inefficient executor service in this - # library. It exists mainly for testing an debugging. Thread creation - # and management is expensive in Ruby and this executor performs no - # resource pooling. This can be very beneficial during testing and - # debugging because it decouples the using code from the underlying - # executor implementation. In production this executor will likely - # lead to suboptimal performance. - # - # @note Intended for use primarily in testing and debugging. - class SimpleExecutorService < RubyExecutorService - - # @!macro executor_service_method_post - def self.post(*args) - raise ArgumentError.new('no block given') unless block_given? - Thread.new(*args) do - Thread.current.abort_on_exception = false - yield(*args) - end - true - end - - # @!macro executor_service_method_left_shift - def self.<<(task) - post(&task) - self - end - - # @!macro executor_service_method_post - def post(*args, &task) - raise ArgumentError.new('no block given') unless block_given? - return false unless running? - @count.increment - Thread.new(*args) do - Thread.current.abort_on_exception = false - begin - yield(*args) - ensure - @count.decrement - @stopped.set if @running.false? && @count.value == 0 - end - end - end - - # @!macro executor_service_method_left_shift - def <<(task) - post(&task) - self - end - - # @!macro executor_service_method_running_question - def running? - @running.true? - end - - # @!macro executor_service_method_shuttingdown_question - def shuttingdown? - @running.false? && ! @stopped.set? - end - - # @!macro executor_service_method_shutdown_question - def shutdown? - @stopped.set? - end - - # @!macro executor_service_method_shutdown - def shutdown - @running.make_false - @stopped.set if @count.value == 0 - true - end - - # @!macro executor_service_method_kill - def kill - @running.make_false - @stopped.set - true - end - - # @!macro executor_service_method_wait_for_termination - def wait_for_termination(timeout = nil) - @stopped.wait(timeout) - end - - private - - def ns_initialize - @running = Concurrent::AtomicBoolean.new(true) - @stopped = Concurrent::Event.new - @count = Concurrent::AtomicFixnum.new(0) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/single_thread_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -require 'concurrent/executor/ruby_single_thread_executor' - -module Concurrent - - if Concurrent.on_jruby? - require 'concurrent/executor/java_single_thread_executor' - end - - SingleThreadExecutorImplementation = case - when Concurrent.on_jruby? - JavaSingleThreadExecutor - else - RubySingleThreadExecutor - end - private_constant :SingleThreadExecutorImplementation - - # @!macro single_thread_executor - # - # A thread pool with a single thread an unlimited queue. Should the thread - # die for any reason it will be removed and replaced, thus ensuring that - # the executor will always remain viable and available to process jobs. - # - # A common pattern for background processing is to create a single thread - # on which an infinite loop is run. The thread's loop blocks on an input - # source (perhaps blocking I/O or a queue) and processes each input as it - # is received. This pattern has several issues. The thread itself is highly - # susceptible to errors during processing. Also, the thread itself must be - # constantly monitored and restarted should it die. `SingleThreadExecutor` - # encapsulates all these bahaviors. The task processor is highly resilient - # to errors from within tasks. Also, should the thread die it will - # automatically be restarted. - # - # The API and behavior of this class are based on Java's `SingleThreadExecutor`. - # - # @!macro abstract_executor_service_public_api - class SingleThreadExecutor < SingleThreadExecutorImplementation - - # @!macro single_thread_executor_method_initialize - # - # Create a new thread pool. - # - # @option opts [Symbol] :fallback_policy (:discard) the policy for handling new - # tasks that are received when the queue size has reached - # `max_queue` or the executor has shut down - # - # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified - # in `FALLBACK_POLICIES` - # - # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html - # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html - - # @!method initialize(opts = {}) - # @!macro single_thread_executor_method_initialize - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/thread_pool_executor.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -require 'concurrent/utility/engine' -require 'concurrent/executor/ruby_thread_pool_executor' - -module Concurrent - - if Concurrent.on_jruby? - require 'concurrent/executor/java_thread_pool_executor' - end - - ThreadPoolExecutorImplementation = case - when Concurrent.on_jruby? - JavaThreadPoolExecutor - else - RubyThreadPoolExecutor - end - private_constant :ThreadPoolExecutorImplementation - - # @!macro thread_pool_executor - # - # An abstraction composed of one or more threads and a task queue. Tasks - # (blocks or `proc` objects) are submitted to the pool and added to the queue. - # The threads in the pool remove the tasks and execute them in the order - # they were received. - # - # A `ThreadPoolExecutor` will automatically adjust the pool size according - # to the bounds set by `min-threads` and `max-threads`. When a new task is - # submitted and fewer than `min-threads` threads are running, a new thread - # is created to handle the request, even if other worker threads are idle. - # If there are more than `min-threads` but less than `max-threads` threads - # running, a new thread will be created only if the queue is full. - # - # Threads that are idle for too long will be garbage collected, down to the - # configured minimum options. Should a thread crash it, too, will be garbage collected. - # - # `ThreadPoolExecutor` is based on the Java class of the same name. From - # the official Java documentation; - # - # > Thread pools address two different problems: they usually provide - # > improved performance when executing large numbers of asynchronous tasks, - # > due to reduced per-task invocation overhead, and they provide a means - # > of bounding and managing the resources, including threads, consumed - # > when executing a collection of tasks. Each ThreadPoolExecutor also - # > maintains some basic statistics, such as the number of completed tasks. - # > - # > To be useful across a wide range of contexts, this class provides many - # > adjustable parameters and extensibility hooks. However, programmers are - # > urged to use the more convenient Executors factory methods - # > [CachedThreadPool] (unbounded thread pool, with automatic thread reclamation), - # > [FixedThreadPool] (fixed size thread pool) and [SingleThreadExecutor] (single - # > background thread), that preconfigure settings for the most common usage - # > scenarios. - # - # @!macro thread_pool_options - # - # @!macro thread_pool_executor_public_api - class ThreadPoolExecutor < ThreadPoolExecutorImplementation - - # @!macro thread_pool_executor_method_initialize - # - # Create a new thread pool. - # - # @param [Hash] opts the options which configure the thread pool. - # - # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum - # number of threads to be created - # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted - # and fewer than `min_threads` are running, a new thread is created - # @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum - # number of seconds a thread may be idle before being reclaimed - # @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum - # number of tasks allowed in the work queue at any one time; a value of - # zero means the queue may grow without bound - # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new - # tasks that are received when the queue size has reached - # `max_queue` or the executor has shut down - # - # @raise [ArgumentError] if `:max_threads` is less than one - # @raise [ArgumentError] if `:min_threads` is less than zero - # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified - # in `FALLBACK_POLICIES` - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html - - # @!method initialize(opts = {}) - # @!macro thread_pool_executor_method_initialize - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executor/timer_set.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,173 +0,0 @@ -require 'concurrent/scheduled_task' -require 'concurrent/atomic/event' -require 'concurrent/collection/non_concurrent_priority_queue' -require 'concurrent/executor/executor_service' -require 'concurrent/executor/single_thread_executor' - -require 'concurrent/options' - -module Concurrent - - # Executes a collection of tasks, each after a given delay. A master task - # monitors the set and schedules each task for execution at the appropriate - # time. Tasks are run on the global thread pool or on the supplied executor. - # Each task is represented as a `ScheduledTask`. - # - # @see Concurrent::ScheduledTask - # - # @!macro monotonic_clock_warning - class TimerSet < RubyExecutorService - - # Create a new set of timed tasks. - # - # @!macro executor_options - # - # @param [Hash] opts the options used to specify the executor on which to perform actions - # @option opts [Executor] :executor when set use the given `Executor` instance. - # Three special values are also supported: `:task` returns the global task pool, - # `:operation` returns the global operation pool, and `:immediate` returns a new - # `ImmediateExecutor` object. - def initialize(opts = {}) - super(opts) - end - - # Post a task to be execute run after a given delay (in seconds). If the - # delay is less than 1/100th of a second the task will be immediately post - # to the executor. - # - # @param [Float] delay the number of seconds to wait for before executing the task. - # @param [Array] args the arguments passed to the task on execution. - # - # @yield the task to be performed. - # - # @return [Concurrent::ScheduledTask, false] IVar representing the task if the post - # is successful; false after shutdown. - # - # @raise [ArgumentError] if the intended execution time is not in the future. - # @raise [ArgumentError] if no block is given. - def post(delay, *args, &task) - raise ArgumentError.new('no block given') unless block_given? - return false unless running? - opts = { executor: @task_executor, - args: args, - timer_set: self } - task = ScheduledTask.execute(delay, opts, &task) # may raise exception - task.unscheduled? ? false : task - end - - # Begin an immediate shutdown. In-progress tasks will be allowed to - # complete but enqueued tasks will be dismissed and no new tasks - # will be accepted. Has no additional effect if the thread pool is - # not running. - def kill - shutdown - end - - private :<< - - private - - # Initialize the object. - # - # @param [Hash] opts the options to create the object with. - # @!visibility private - def ns_initialize(opts) - @queue = Collection::NonConcurrentPriorityQueue.new(order: :min) - @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor - @timer_executor = SingleThreadExecutor.new - @condition = Event.new - @ruby_pid = $$ # detects if Ruby has forked - self.auto_terminate = opts.fetch(:auto_terminate, true) - end - - # Post the task to the internal queue. - # - # @note This is intended as a callback method from ScheduledTask - # only. It is not intended to be used directly. Post a task - # by using the `SchedulesTask#execute` method. - # - # @!visibility private - def post_task(task) - synchronize { ns_post_task(task) } - end - - # @!visibility private - def ns_post_task(task) - return false unless ns_running? - ns_reset_if_forked - if (task.initial_delay) <= 0.01 - task.executor.post { task.process_task } - else - @queue.push(task) - # only post the process method when the queue is empty - @timer_executor.post(&method(:process_tasks)) if @queue.length == 1 - @condition.set - end - true - end - - # Remove the given task from the queue. - # - # @note This is intended as a callback method from `ScheduledTask` - # only. It is not intended to be used directly. Cancel a task - # by using the `ScheduledTask#cancel` method. - # - # @!visibility private - def remove_task(task) - synchronize { @queue.delete(task) } - end - - # `ExecutorService` callback called during shutdown. - # - # @!visibility private - def ns_shutdown_execution - ns_reset_if_forked - @queue.clear - @timer_executor.kill - stopped_event.set - end - - def ns_reset_if_forked - if $$ != @ruby_pid - @queue.clear - @condition.reset - @ruby_pid = $$ - end - end - - # Run a loop and execute tasks in the scheduled order and at the approximate - # scheduled time. If no tasks remain the thread will exit gracefully so that - # garbage collection can occur. If there are no ready tasks it will sleep - # for up to 60 seconds waiting for the next scheduled task. - # - # @!visibility private - def process_tasks - loop do - task = synchronize { @condition.reset; @queue.peek } - break unless task - - now = Concurrent.monotonic_time - diff = task.schedule_time - now - - if diff <= 0 - # We need to remove the task from the queue before passing - # it to the executor, to avoid race conditions where we pass - # the peek'ed task to the executor and then pop a different - # one that's been added in the meantime. - # - # Note that there's no race condition between the peek and - # this pop - this pop could retrieve a different task from - # the peek, but that task would be due to fire now anyway - # (because @queue is a priority queue, and this thread is - # the only reader, so whatever timer is at the head of the - # queue now must have the same pop time, or a closer one, as - # when we peeked). - task = synchronize { @queue.pop } - task.executor.post { task.process_task } - else - @condition.wait([diff, 60].min) - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/executors.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -require 'concurrent/executor/abstract_executor_service' -require 'concurrent/executor/cached_thread_pool' -require 'concurrent/executor/executor_service' -require 'concurrent/executor/fixed_thread_pool' -require 'concurrent/executor/immediate_executor' -require 'concurrent/executor/indirect_immediate_executor' -require 'concurrent/executor/java_executor_service' -require 'concurrent/executor/java_single_thread_executor' -require 'concurrent/executor/java_thread_pool_executor' -require 'concurrent/executor/ruby_executor_service' -require 'concurrent/executor/ruby_single_thread_executor' -require 'concurrent/executor/ruby_thread_pool_executor' -require 'concurrent/executor/cached_thread_pool' -require 'concurrent/executor/safe_task_executor' -require 'concurrent/executor/serial_executor_service' -require 'concurrent/executor/serialized_execution' -require 'concurrent/executor/serialized_execution_delegator' -require 'concurrent/executor/single_thread_executor' -require 'concurrent/executor/thread_pool_executor' -require 'concurrent/executor/timer_set' diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/future.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -require 'thread' -require 'concurrent/constants' -require 'concurrent/errors' -require 'concurrent/ivar' -require 'concurrent/executor/safe_task_executor' - -require 'concurrent/options' - -# TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc. - - -module Concurrent - - # {include:file:docs-source/future.md} - # - # @!macro copy_options - # - # @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module - # @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future - class Future < IVar - - # Create a new `Future` in the `:unscheduled` state. - # - # @yield the asynchronous operation to perform - # - # @!macro executor_and_deref_options - # - # @option opts [object, Array] :args zero or more arguments to be passed the task - # block on execution - # - # @raise [ArgumentError] if no block is given - def initialize(opts = {}, &block) - raise ArgumentError.new('no block given') unless block_given? - super(NULL, opts.merge(__task_from_block__: block), &nil) - end - - # Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and - # passes the block to a new thread/thread pool for eventual execution. - # Does nothing if the `Future` is in any state other than `:unscheduled`. - # - # @return [Future] a reference to `self` - # - # @example Instance and execute in separate steps - # future = Concurrent::Future.new{ sleep(1); 42 } - # future.state #=> :unscheduled - # future.execute - # future.state #=> :pending - # - # @example Instance and execute in one line - # future = Concurrent::Future.new{ sleep(1); 42 }.execute - # future.state #=> :pending - def execute - if compare_and_set_state(:pending, :unscheduled) - @executor.post{ safe_execute(@task, @args) } - self - end - end - - # Create a new `Future` object with the given block, execute it, and return the - # `:pending` object. - # - # @yield the asynchronous operation to perform - # - # @!macro executor_and_deref_options - # - # @option opts [object, Array] :args zero or more arguments to be passed the task - # block on execution - # - # @raise [ArgumentError] if no block is given - # - # @return [Future] the newly created `Future` in the `:pending` state - # - # @example - # future = Concurrent::Future.execute{ sleep(1); 42 } - # future.state #=> :pending - def self.execute(opts = {}, &block) - Future.new(opts, &block).execute - end - - # @!macro ivar_set_method - def set(value = NULL, &block) - check_for_block_or_value!(block_given?, value) - synchronize do - if @state != :unscheduled - raise MultipleAssignmentError - else - @task = block || Proc.new { value } - end - end - execute - end - - # Attempt to cancel the operation if it has not already processed. - # The operation can only be cancelled while still `pending`. It cannot - # be cancelled once it has begun processing or has completed. - # - # @return [Boolean] was the operation successfully cancelled. - def cancel - if compare_and_set_state(:cancelled, :pending) - complete(false, nil, CancelledOperationError.new) - true - else - false - end - end - - # Has the operation been successfully cancelled? - # - # @return [Boolean] - def cancelled? - state == :cancelled - end - - # Wait the given number of seconds for the operation to complete. - # On timeout attempt to cancel the operation. - # - # @param [Numeric] timeout the maximum time in seconds to wait. - # @return [Boolean] true if the operation completed before the timeout - # else false - def wait_or_cancel(timeout) - wait(timeout) - if complete? - true - else - cancel - false - end - end - - protected - - def ns_initialize(value, opts) - super - @state = :unscheduled - @task = opts[:__task_from_block__] - @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor - @args = get_arguments_from(opts) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/hash.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -require 'concurrent/utility/engine' -require 'concurrent/thread_safe/util' - -module Concurrent - - # @!macro concurrent_hash - # - # A thread-safe subclass of Hash. This version locks against the object - # itself for every method call, ensuring only one thread can be reading - # or writing at a time. This includes iteration methods like `#each`, - # which takes the lock repeatedly when reading an item. - # - # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash` - - # @!macro internal_implementation_note - HashImplementation = case - when Concurrent.on_cruby? - # Hash is thread-safe in practice because CRuby runs - # threads one at a time and does not do context - # switching during the execution of C functions. - ::Hash - - when Concurrent.on_jruby? - require 'jruby/synchronized' - - class JRubyHash < ::Hash - include JRuby::Synchronized - end - JRubyHash - - when Concurrent.on_rbx? - require 'monitor' - require 'concurrent/thread_safe/util/data_structures' - - class RbxHash < ::Hash - end - ThreadSafe::Util.make_synchronized_on_rbx RbxHash - RbxHash - - when Concurrent.on_truffleruby? - require 'concurrent/thread_safe/util/data_structures' - - class TruffleRubyHash < ::Hash - end - - ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyHash - TruffleRubyHash - - else - warn 'Possibly unsupported Ruby implementation' - ::Hash - end - private_constant :HashImplementation - - # @!macro concurrent_hash - class Hash < HashImplementation - end - -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/immutable_struct.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -require 'concurrent/synchronization/abstract_struct' -require 'concurrent/synchronization' - -module Concurrent - - # A thread-safe, immutable variation of Ruby's standard `Struct`. - # - # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct` - module ImmutableStruct - include Synchronization::AbstractStruct - - def self.included(base) - base.safe_initialization! - end - - # @!macro struct_values - def values - ns_values - end - - alias_method :to_a, :values - - # @!macro struct_values_at - def values_at(*indexes) - ns_values_at(indexes) - end - - # @!macro struct_inspect - def inspect - ns_inspect - end - - alias_method :to_s, :inspect - - # @!macro struct_merge - def merge(other, &block) - ns_merge(other, &block) - end - - # @!macro struct_to_h - def to_h - ns_to_h - end - - # @!macro struct_get - def [](member) - ns_get(member) - end - - # @!macro struct_equality - def ==(other) - ns_equality(other) - end - - # @!macro struct_each - def each(&block) - return enum_for(:each) unless block_given? - ns_each(&block) - end - - # @!macro struct_each_pair - def each_pair(&block) - return enum_for(:each_pair) unless block_given? - ns_each_pair(&block) - end - - # @!macro struct_select - def select(&block) - return enum_for(:select) unless block_given? - ns_select(&block) - end - - # @!macro struct_new - def self.new(*args, &block) - clazz_name = nil - if args.length == 0 - raise ArgumentError.new('wrong number of arguments (0 for 1+)') - elsif args.length > 0 && args.first.is_a?(String) - clazz_name = args.shift - end - FACTORY.define_struct(clazz_name, args, &block) - end - - FACTORY = Class.new(Synchronization::LockableObject) do - def define_struct(name, members, &block) - synchronize do - Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block) - end - end - end.new - private_constant :FACTORY - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/ivar.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ -require 'concurrent/constants' -require 'concurrent/errors' -require 'concurrent/collection/copy_on_write_observer_set' -require 'concurrent/concern/obligation' -require 'concurrent/concern/observable' -require 'concurrent/synchronization' - -module Concurrent - - # An `IVar` is like a future that you can assign. As a future is a value that - # is being computed that you can wait on, an `IVar` is a value that is waiting - # to be assigned, that you can wait on. `IVars` are single assignment and - # deterministic. - # - # Then, express futures as an asynchronous computation that assigns an `IVar`. - # The `IVar` becomes the primitive on which [futures](Future) and - # [dataflow](Dataflow) are built. - # - # An `IVar` is a single-element container that is normally created empty, and - # can only be set once. The I in `IVar` stands for immutable. Reading an - # `IVar` normally blocks until it is set. It is safe to set and read an `IVar` - # from different threads. - # - # If you want to have some parallel task set the value in an `IVar`, you want - # a `Future`. If you want to create a graph of parallel tasks all executed - # when the values they depend on are ready you want `dataflow`. `IVar` is - # generally a low-level primitive. - # - # ## Examples - # - # Create, set and get an `IVar` - # - # ```ruby - # ivar = Concurrent::IVar.new - # ivar.set 14 - # ivar.value #=> 14 - # ivar.set 2 # would now be an error - # ``` - # - # ## See Also - # - # 1. For the theory: Arvind, R. Nikhil, and K. Pingali. - # [I-Structures: Data structures for parallel computing](http://dl.acm.org/citation.cfm?id=69562). - # In Proceedings of Workshop on Graph Reduction, 1986. - # 2. For recent application: - # [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html). - class IVar < Synchronization::LockableObject - include Concern::Obligation - include Concern::Observable - - # Create a new `IVar` in the `:pending` state with the (optional) initial value. - # - # @param [Object] value the initial value - # @param [Hash] opts the options to create a message with - # @option opts [String] :dup_on_deref (false) call `#dup` before returning - # the data - # @option opts [String] :freeze_on_deref (false) call `#freeze` before - # returning the data - # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing - # the internal value and returning the value returned from the proc - def initialize(value = NULL, opts = {}, &block) - if value != NULL && block_given? - raise ArgumentError.new('provide only a value or a block') - end - super(&nil) - synchronize { ns_initialize(value, opts, &block) } - end - - # Add an observer on this object that will receive notification on update. - # - # Upon completion the `IVar` will notify all observers in a thread-safe way. - # The `func` method of the observer will be called with three arguments: the - # `Time` at which the `Future` completed the asynchronous operation, the - # final `value` (or `nil` on rejection), and the final `reason` (or `nil` on - # fulfillment). - # - # @param [Object] observer the object that will be notified of changes - # @param [Symbol] func symbol naming the method to call when this - # `Observable` has changes` - def add_observer(observer = nil, func = :update, &block) - raise ArgumentError.new('cannot provide both an observer and a block') if observer && block - direct_notification = false - - if block - observer = block - func = :call - end - - synchronize do - if event.set? - direct_notification = true - else - observers.add_observer(observer, func) - end - end - - observer.send(func, Time.now, self.value, reason) if direct_notification - observer - end - - # @!macro ivar_set_method - # Set the `IVar` to a value and wake or notify all threads waiting on it. - # - # @!macro ivar_set_parameters_and_exceptions - # @param [Object] value the value to store in the `IVar` - # @yield A block operation to use for setting the value - # @raise [ArgumentError] if both a value and a block are given - # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already - # been set or otherwise completed - # - # @return [IVar] self - def set(value = NULL) - check_for_block_or_value!(block_given?, value) - raise MultipleAssignmentError unless compare_and_set_state(:processing, :pending) - - begin - value = yield if block_given? - complete_without_notification(true, value, nil) - rescue => ex - complete_without_notification(false, nil, ex) - end - - notify_observers(self.value, reason) - self - end - - # @!macro ivar_fail_method - # Set the `IVar` to failed due to some error and wake or notify all threads waiting on it. - # - # @param [Object] reason for the failure - # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already - # been set or otherwise completed - # @return [IVar] self - def fail(reason = StandardError.new) - complete(false, nil, reason) - end - - # Attempt to set the `IVar` with the given value or block. Return a - # boolean indicating the success or failure of the set operation. - # - # @!macro ivar_set_parameters_and_exceptions - # - # @return [Boolean] true if the value was set else false - def try_set(value = NULL, &block) - set(value, &block) - true - rescue MultipleAssignmentError - false - end - - protected - - # @!visibility private - def ns_initialize(value, opts) - value = yield if block_given? - init_obligation - self.observers = Collection::CopyOnWriteObserverSet.new - set_deref_options(opts) - - @state = :pending - if value != NULL - ns_complete_without_notification(true, value, nil) - end - end - - # @!visibility private - def safe_execute(task, args = []) - if compare_and_set_state(:processing, :pending) - success, val, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args) - complete(success, val, reason) - yield(success, val, reason) if block_given? - end - end - - # @!visibility private - def complete(success, value, reason) - complete_without_notification(success, value, reason) - notify_observers(self.value, reason) - self - end - - # @!visibility private - def complete_without_notification(success, value, reason) - synchronize { ns_complete_without_notification(success, value, reason) } - self - end - - # @!visibility private - def notify_observers(value, reason) - observers.notify_and_delete_observers{ [Time.now, value, reason] } - end - - # @!visibility private - def ns_complete_without_notification(success, value, reason) - raise MultipleAssignmentError if [:fulfilled, :rejected].include? @state - set_state(success, value, reason) - event.set - end - - # @!visibility private - def check_for_block_or_value!(block_given, value) # :nodoc: - if (block_given && value != NULL) || (! block_given && value == NULL) - raise ArgumentError.new('must set with either a value or a block') - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/map.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,337 +0,0 @@ -require 'thread' -require 'concurrent/constants' -require 'concurrent/synchronization' -require 'concurrent/utility/engine' - -module Concurrent - # @!visibility private - module Collection - - # @!visibility private - MapImplementation = case - when Concurrent.on_jruby? - # noinspection RubyResolve - JRubyMapBackend - when Concurrent.on_cruby? - require 'concurrent/collection/map/mri_map_backend' - MriMapBackend - when Concurrent.on_rbx? || Concurrent.on_truffleruby? - require 'concurrent/collection/map/atomic_reference_map_backend' - AtomicReferenceMapBackend - else - warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation' - require 'concurrent/collection/map/synchronized_map_backend' - SynchronizedMapBackend - end - end - - # `Concurrent::Map` is a hash-like object and should have much better performance - # characteristics, especially under high concurrency, than `Concurrent::Hash`. - # However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash` - # -- for instance, it does not necessarily retain ordering by insertion time as `Hash` - # does. For most uses it should do fine though, and we recommend you consider - # `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs. - class Map < Collection::MapImplementation - - # @!macro map.atomic_method - # This method is atomic. - - # @!macro map.atomic_method_with_block - # This method is atomic. - # @note Atomic methods taking a block do not allow the `self` instance - # to be used within the block. Doing so will cause a deadlock. - - # @!method compute_if_absent(key) - # Compute and store new value for key if the key is absent. - # @param [Object] key - # @yield new value - # @yieldreturn [Object] new value - # @return [Object] new value or current value - # @!macro map.atomic_method_with_block - - # @!method compute_if_present(key) - # Compute and store new value for key if the key is present. - # @param [Object] key - # @yield new value - # @yieldparam old_value [Object] - # @yieldreturn [Object, nil] new value, when nil the key is removed - # @return [Object, nil] new value or nil - # @!macro map.atomic_method_with_block - - # @!method compute(key) - # Compute and store new value for key. - # @param [Object] key - # @yield compute new value from old one - # @yieldparam old_value [Object, nil] old_value, or nil when key is absent - # @yieldreturn [Object, nil] new value, when nil the key is removed - # @return [Object, nil] new value or nil - # @!macro map.atomic_method_with_block - - # @!method merge_pair(key, value) - # If the key is absent, the value is stored, otherwise new value is - # computed with a block. - # @param [Object] key - # @param [Object] value - # @yield compute new value from old one - # @yieldparam old_value [Object] old value - # @yieldreturn [Object, nil] new value, when nil the key is removed - # @return [Object, nil] new value or nil - # @!macro map.atomic_method_with_block - - # @!method replace_pair(key, old_value, new_value) - # Replaces old_value with new_value if key exists and current value - # matches old_value - # @param [Object] key - # @param [Object] old_value - # @param [Object] new_value - # @return [true, false] true if replaced - # @!macro map.atomic_method - - # @!method replace_if_exists(key, new_value) - # Replaces current value with new_value if key exists - # @param [Object] key - # @param [Object] new_value - # @return [Object, nil] old value or nil - # @!macro map.atomic_method - - # @!method get_and_set(key, value) - # Get the current value under key and set new value. - # @param [Object] key - # @param [Object] value - # @return [Object, nil] old value or nil when the key was absent - # @!macro map.atomic_method - - # @!method delete(key) - # Delete key and its value. - # @param [Object] key - # @return [Object, nil] old value or nil when the key was absent - # @!macro map.atomic_method - - # @!method delete_pair(key, value) - # Delete pair and its value if current value equals the provided value. - # @param [Object] key - # @param [Object] value - # @return [true, false] true if deleted - # @!macro map.atomic_method - - - def initialize(options = nil, &block) - if options.kind_of?(::Hash) - validate_options_hash!(options) - else - options = nil - end - - super(options) - @default_proc = block - end - - # Get a value with key - # @param [Object] key - # @return [Object] the value - def [](key) - if value = super # non-falsy value is an existing mapping, return it right away - value - # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call - # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value - # would be returned) - # note: nil == value check is not technically necessary - elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL)) - @default_proc.call(self, key) - else - value - end - end - - alias_method :get, :[] - # TODO (pitr-ch 30-Oct-2018): doc - alias_method :put, :[]= - - # Get a value with key, or default_value when key is absent, - # or fail when no default_value is given. - # @param [Object] key - # @param [Object] default_value - # @yield default value for a key - # @yieldparam key [Object] - # @yieldreturn [Object] default value - # @return [Object] the value or default value - # @raise [KeyError] when key is missing and no default_value is provided - # @!macro map_method_not_atomic - # @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended - # to be use as a concurrency primitive with strong happens-before - # guarantees. It is not intended to be used as a high-level abstraction - # supporting complex operations. All read and write operations are - # thread safe, but no guarantees are made regarding race conditions - # between the fetch operation and yielding to the block. Additionally, - # this method does not support recursion. This is due to internal - # constraints that are very unlikely to change in the near future. - def fetch(key, default_value = NULL) - if NULL != (value = get_or_default(key, NULL)) - value - elsif block_given? - yield key - elsif NULL != default_value - default_value - else - raise_fetch_no_key - end - end - - # Fetch value with key, or store default value when key is absent, - # or fail when no default_value is given. This is a two step operation, - # therefore not atomic. The store can overwrite other concurrently - # stored value. - # @param [Object] key - # @param [Object] default_value - # @yield default value for a key - # @yieldparam key [Object] - # @yieldreturn [Object] default value - # @return [Object] the value or default value - # @!macro map.atomic_method_with_block - def fetch_or_store(key, default_value = NULL) - fetch(key) do - put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value)) - end - end - - # Insert value into map with key if key is absent in one atomic step. - # @param [Object] key - # @param [Object] value - # @return [Object, nil] the value or nil when key was present - def put_if_absent(key, value) - computed = false - result = compute_if_absent(key) do - computed = true - value - end - computed ? nil : result - end unless method_defined?(:put_if_absent) - - # Is the value stored in the map. Iterates over all values. - # @param [Object] value - # @return [true, false] - def value?(value) - each_value do |v| - return true if value.equal?(v) - end - false - end - - # All keys - # @return [::Array] keys - def keys - arr = [] - each_pair { |k, v| arr << k } - arr - end unless method_defined?(:keys) - - # All values - # @return [::Array] values - def values - arr = [] - each_pair { |k, v| arr << v } - arr - end unless method_defined?(:values) - - # Iterates over each key. - # @yield for each key in the map - # @yieldparam key [Object] - # @return [self] - # @!macro map.atomic_method_with_block - def each_key - each_pair { |k, v| yield k } - end unless method_defined?(:each_key) - - # Iterates over each value. - # @yield for each value in the map - # @yieldparam value [Object] - # @return [self] - # @!macro map.atomic_method_with_block - def each_value - each_pair { |k, v| yield v } - end unless method_defined?(:each_value) - - # Iterates over each key value pair. - # @yield for each key value pair in the map - # @yieldparam key [Object] - # @yieldparam value [Object] - # @return [self] - # @!macro map.atomic_method_with_block - def each_pair - return enum_for :each_pair unless block_given? - super - end - - alias_method :each, :each_pair unless method_defined?(:each) - - # Find key of a value. - # @param [Object] value - # @return [Object, nil] key or nil when not found - def key(value) - each_pair { |k, v| return k if v == value } - nil - end unless method_defined?(:key) - alias_method :index, :key if RUBY_VERSION < '1.9' - - # Is map empty? - # @return [true, false] - def empty? - each_pair { |k, v| return false } - true - end unless method_defined?(:empty?) - - # The size of map. - # @return [Integer] size - def size - count = 0 - each_pair { |k, v| count += 1 } - count - end unless method_defined?(:size) - - # @!visibility private - def marshal_dump - raise TypeError, "can't dump hash with default proc" if @default_proc - h = {} - each_pair { |k, v| h[k] = v } - h - end - - # @!visibility private - def marshal_load(hash) - initialize - populate_from(hash) - end - - undef :freeze - - # @!visibility private - def inspect - format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect - end - - private - - def raise_fetch_no_key - raise KeyError, 'key not found' - end - - def initialize_copy(other) - super - populate_from(other) - end - - def populate_from(hash) - hash.each_pair { |k, v| self[k] = v } - self - end - - def validate_options_hash!(options) - if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0) - raise ArgumentError, ":initial_capacity must be a positive Integer" - end - if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1) - raise ArgumentError, ":load_factor must be a number between 0 and 1" - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/maybe.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,229 +0,0 @@ -require 'concurrent/synchronization' - -module Concurrent - - # A `Maybe` encapsulates an optional value. A `Maybe` either contains a value - # of (represented as `Just`), or it is empty (represented as `Nothing`). Using - # `Maybe` is a good way to deal with errors or exceptional cases without - # resorting to drastic measures such as exceptions. - # - # `Maybe` is a replacement for the use of `nil` with better type checking. - # - # For compatibility with {Concurrent::Concern::Obligation} the predicate and - # accessor methods are aliased as `fulfilled?`, `rejected?`, `value`, and - # `reason`. - # - # ## Motivation - # - # A common pattern in languages with pattern matching, such as Erlang and - # Haskell, is to return *either* a value *or* an error from a function - # Consider this Erlang code: - # - # ```erlang - # case file:consult("data.dat") of - # {ok, Terms} -> do_something_useful(Terms); - # {error, Reason} -> lager:error(Reason) - # end. - # ``` - # - # In this example the standard library function `file:consult` returns a - # [tuple](http://erlang.org/doc/reference_manual/data_types.html#id69044) - # with two elements: an [atom](http://erlang.org/doc/reference_manual/data_types.html#id64134) - # (similar to a ruby symbol) and a variable containing ancillary data. On - # success it returns the atom `ok` and the data from the file. On failure it - # returns `error` and a string with an explanation of the problem. With this - # pattern there is no ambiguity regarding success or failure. If the file is - # empty the return value cannot be misinterpreted as an error. And when an - # error occurs the return value provides useful information. - # - # In Ruby we tend to return `nil` when an error occurs or else we raise an - # exception. Both of these idioms are problematic. Returning `nil` is - # ambiguous because `nil` may also be a valid value. It also lacks - # information pertaining to the nature of the error. Raising an exception - # is both expensive and usurps the normal flow of control. All of these - # problems can be solved with the use of a `Maybe`. - # - # A `Maybe` is unambiguous with regard to whether or not it contains a value. - # When `Just` it contains a value, when `Nothing` it does not. When `Just` - # the value it contains may be `nil`, which is perfectly valid. When - # `Nothing` the reason for the lack of a value is contained as well. The - # previous Erlang example can be duplicated in Ruby in a principled way by - # having functions return `Maybe` objects: - # - # ```ruby - # result = MyFileUtils.consult("data.dat") # returns a Maybe - # if result.just? - # do_something_useful(result.value) # or result.just - # else - # logger.error(result.reason) # or result.nothing - # end - # ``` - # - # @example Returning a Maybe from a Function - # module MyFileUtils - # def self.consult(path) - # file = File.open(path, 'r') - # Concurrent::Maybe.just(file.read) - # rescue => ex - # return Concurrent::Maybe.nothing(ex) - # ensure - # file.close if file - # end - # end - # - # maybe = MyFileUtils.consult('bogus.file') - # maybe.just? #=> false - # maybe.nothing? #=> true - # maybe.reason #=> # - # - # maybe = MyFileUtils.consult('README.md') - # maybe.just? #=> true - # maybe.nothing? #=> false - # maybe.value #=> "# Concurrent Ruby\n[![Gem Version..." - # - # @example Using Maybe with a Block - # result = Concurrent::Maybe.from do - # Client.find(10) # Client is an ActiveRecord model - # end - # - # # -- if the record was found - # result.just? #=> true - # result.value #=> # - # - # # -- if the record was not found - # result.just? #=> false - # result.reason #=> ActiveRecord::RecordNotFound - # - # @example Using Maybe with the Null Object Pattern - # # In a Rails controller... - # result = ClientService.new(10).find # returns a Maybe - # render json: result.or(NullClient.new) - # - # @see https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html Haskell Data.Maybe - # @see https://github.com/purescript/purescript-maybe/blob/master/docs/Data.Maybe.md PureScript Data.Maybe - class Maybe < Synchronization::Object - include Comparable - safe_initialization! - - # Indicates that the given attribute has not been set. - # When `Just` the {#nothing} getter will return `NONE`. - # When `Nothing` the {#just} getter will return `NONE`. - NONE = ::Object.new.freeze - - # The value of a `Maybe` when `Just`. Will be `NONE` when `Nothing`. - attr_reader :just - - # The reason for the `Maybe` when `Nothing`. Will be `NONE` when `Just`. - attr_reader :nothing - - private_class_method :new - - # Create a new `Maybe` using the given block. - # - # Runs the given block passing all function arguments to the block as block - # arguments. If the block runs to completion without raising an exception - # a new `Just` is created with the value set to the return value of the - # block. If the block raises an exception a new `Nothing` is created with - # the reason being set to the raised exception. - # - # @param [Array] args Zero or more arguments to pass to the block. - # @yield The block from which to create a new `Maybe`. - # @yieldparam [Array] args Zero or more block arguments passed as - # arguments to the function. - # - # @return [Maybe] The newly created object. - # - # @raise [ArgumentError] when no block given. - def self.from(*args) - raise ArgumentError.new('no block given') unless block_given? - begin - value = yield(*args) - return new(value, NONE) - rescue => ex - return new(NONE, ex) - end - end - - # Create a new `Just` with the given value. - # - # @param [Object] value The value to set for the new `Maybe` object. - # - # @return [Maybe] The newly created object. - def self.just(value) - return new(value, NONE) - end - - # Create a new `Nothing` with the given (optional) reason. - # - # @param [Exception] error The reason to set for the new `Maybe` object. - # When given a string a new `StandardError` will be created with the - # argument as the message. When no argument is given a new - # `StandardError` with an empty message will be created. - # - # @return [Maybe] The newly created object. - def self.nothing(error = '') - if error.is_a?(Exception) - nothing = error - else - nothing = StandardError.new(error.to_s) - end - return new(NONE, nothing) - end - - # Is this `Maybe` a `Just` (successfully fulfilled with a value)? - # - # @return [Boolean] True if `Just` or false if `Nothing`. - def just? - ! nothing? - end - alias :fulfilled? :just? - - # Is this `Maybe` a `nothing` (rejected with an exception upon fulfillment)? - # - # @return [Boolean] True if `Nothing` or false if `Just`. - def nothing? - @nothing != NONE - end - alias :rejected? :nothing? - - alias :value :just - - alias :reason :nothing - - # Comparison operator. - # - # @return [Integer] 0 if self and other are both `Nothing`; - # -1 if self is `Nothing` and other is `Just`; - # 1 if self is `Just` and other is nothing; - # `self.just <=> other.just` if both self and other are `Just`. - def <=>(other) - if nothing? - other.nothing? ? 0 : -1 - else - other.nothing? ? 1 : just <=> other.just - end - end - - # Return either the value of self or the given default value. - # - # @return [Object] The value of self when `Just`; else the given default. - def or(other) - just? ? just : other - end - - private - - # Create a new `Maybe` with the given attributes. - # - # @param [Object] just The value when `Just` else `NONE`. - # @param [Exception, Object] nothing The exception when `Nothing` else `NONE`. - # - # @return [Maybe] The new `Maybe`. - # - # @!visibility private - def initialize(just, nothing) - @just = just - @nothing = nothing - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mutable_struct.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,229 +0,0 @@ -require 'concurrent/synchronization/abstract_struct' -require 'concurrent/synchronization' - -module Concurrent - - # An thread-safe variation of Ruby's standard `Struct`. Values can be set at - # construction or safely changed at any time during the object's lifecycle. - # - # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct` - module MutableStruct - include Synchronization::AbstractStruct - - # @!macro struct_new - # - # Factory for creating new struct classes. - # - # ``` - # new([class_name] [, member_name]+>) -> StructClass click to toggle source - # new([class_name] [, member_name]+>) {|StructClass| block } -> StructClass - # new(value, ...) -> obj - # StructClass[value, ...] -> obj - # ``` - # - # The first two forms are used to create a new struct subclass `class_name` - # that can contain a value for each member_name . This subclass can be - # used to create instances of the structure like any other Class . - # - # If the `class_name` is omitted an anonymous struct class will be created. - # Otherwise, the name of this struct will appear as a constant in the struct class, - # so it must be unique for all structs under this base class and must start with a - # capital letter. Assigning a struct class to a constant also gives the class - # the name of the constant. - # - # If a block is given it will be evaluated in the context of `StructClass`, passing - # the created class as a parameter. This is the recommended way to customize a struct. - # Subclassing an anonymous struct creates an extra anonymous class that will never be used. - # - # The last two forms create a new instance of a struct subclass. The number of value - # parameters must be less than or equal to the number of attributes defined for the - # struct. Unset parameters default to nil. Passing more parameters than number of attributes - # will raise an `ArgumentError`. - # - # @see http://ruby-doc.org/core-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new` - - # @!macro struct_values - # - # Returns the values for this struct as an Array. - # - # @return [Array] the values for this struct - # - def values - synchronize { ns_values } - end - alias_method :to_a, :values - - # @!macro struct_values_at - # - # Returns the struct member values for each selector as an Array. - # - # A selector may be either an Integer offset or a Range of offsets (as in `Array#values_at`). - # - # @param [Fixnum, Range] indexes the index(es) from which to obatin the values (in order) - def values_at(*indexes) - synchronize { ns_values_at(indexes) } - end - - # @!macro struct_inspect - # - # Describe the contents of this struct in a string. - # - # @return [String] the contents of this struct in a string - def inspect - synchronize { ns_inspect } - end - alias_method :to_s, :inspect - - # @!macro struct_merge - # - # Returns a new struct containing the contents of `other` and the contents - # of `self`. If no block is specified, the value for entries with duplicate - # keys will be that of `other`. Otherwise the value for each duplicate key - # is determined by calling the block with the key, its value in `self` and - # its value in `other`. - # - # @param [Hash] other the hash from which to set the new values - # @yield an options block for resolving duplicate keys - # @yieldparam [String, Symbol] member the name of the member which is duplicated - # @yieldparam [Object] selfvalue the value of the member in `self` - # @yieldparam [Object] othervalue the value of the member in `other` - # - # @return [Synchronization::AbstractStruct] a new struct with the new values - # - # @raise [ArgumentError] of given a member that is not defined in the struct - def merge(other, &block) - synchronize { ns_merge(other, &block) } - end - - # @!macro struct_to_h - # - # Returns a hash containing the names and values for the struct’s members. - # - # @return [Hash] the names and values for the struct’s members - def to_h - synchronize { ns_to_h } - end - - # @!macro struct_get - # - # Attribute Reference - # - # @param [Symbol, String, Integer] member the string or symbol name of the member - # for which to obtain the value or the member's index - # - # @return [Object] the value of the given struct member or the member at the given index. - # - # @raise [NameError] if the member does not exist - # @raise [IndexError] if the index is out of range. - def [](member) - synchronize { ns_get(member) } - end - - # @!macro struct_equality - # - # Equality - # - # @return [Boolean] true if other has the same struct subclass and has - # equal member values (according to `Object#==`) - def ==(other) - synchronize { ns_equality(other) } - end - - # @!macro struct_each - # - # Yields the value of each struct member in order. If no block is given - # an enumerator is returned. - # - # @yield the operation to be performed on each struct member - # @yieldparam [Object] value each struct value (in order) - def each(&block) - return enum_for(:each) unless block_given? - synchronize { ns_each(&block) } - end - - # @!macro struct_each_pair - # - # Yields the name and value of each struct member in order. If no block is - # given an enumerator is returned. - # - # @yield the operation to be performed on each struct member/value pair - # @yieldparam [Object] member each struct member (in order) - # @yieldparam [Object] value each struct value (in order) - def each_pair(&block) - return enum_for(:each_pair) unless block_given? - synchronize { ns_each_pair(&block) } - end - - # @!macro struct_select - # - # Yields each member value from the struct to the block and returns an Array - # containing the member values from the struct for which the given block - # returns a true value (equivalent to `Enumerable#select`). - # - # @yield the operation to be performed on each struct member - # @yieldparam [Object] value each struct value (in order) - # - # @return [Array] an array containing each value for which the block returns true - def select(&block) - return enum_for(:select) unless block_given? - synchronize { ns_select(&block) } - end - - # @!macro struct_set - # - # Attribute Assignment - # - # Sets the value of the given struct member or the member at the given index. - # - # @param [Symbol, String, Integer] member the string or symbol name of the member - # for which to obtain the value or the member's index - # - # @return [Object] the value of the given struct member or the member at the given index. - # - # @raise [NameError] if the name does not exist - # @raise [IndexError] if the index is out of range. - def []=(member, value) - if member.is_a? Integer - length = synchronize { @values.length } - if member >= length - raise IndexError.new("offset #{member} too large for struct(size:#{length})") - end - synchronize { @values[member] = value } - else - send("#{member}=", value) - end - rescue NoMethodError - raise NameError.new("no member '#{member}' in struct") - end - - # @!macro struct_new - def self.new(*args, &block) - clazz_name = nil - if args.length == 0 - raise ArgumentError.new('wrong number of arguments (0 for 1+)') - elsif args.length > 0 && args.first.is_a?(String) - clazz_name = args.shift - end - FACTORY.define_struct(clazz_name, args, &block) - end - - FACTORY = Class.new(Synchronization::LockableObject) do - def define_struct(name, members, &block) - synchronize do - clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block) - members.each_with_index do |member, index| - clazz.send :remove_method, member - clazz.send(:define_method, member) do - synchronize { @values[index] } - end - clazz.send(:define_method, "#{member}=") do |value| - synchronize { @values[index] = value } - end - end - clazz - end - end - end.new - private_constant :FACTORY - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/mvar.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,242 +0,0 @@ -require 'concurrent/concern/dereferenceable' -require 'concurrent/synchronization' - -module Concurrent - - # An `MVar` is a synchronized single element container. They are empty or - # contain one item. Taking a value from an empty `MVar` blocks, as does - # putting a value into a full one. You can either think of them as blocking - # queue of length one, or a special kind of mutable variable. - # - # On top of the fundamental `#put` and `#take` operations, we also provide a - # `#mutate` that is atomic with respect to operations on the same instance. - # These operations all support timeouts. - # - # We also support non-blocking operations `#try_put!` and `#try_take!`, a - # `#set!` that ignores existing values, a `#value` that returns the value - # without removing it or returns `MVar::EMPTY`, and a `#modify!` that yields - # `MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`. - # You shouldn't use these operations in the first instance. - # - # `MVar` is a [Dereferenceable](Dereferenceable). - # - # `MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala. - # - # Note that unlike the original Haskell paper, our `#take` is blocking. This is how - # Haskell and Scala do it today. - # - # @!macro copy_options - # - # ## See Also - # - # 1. P. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non- strict, functional language with state](http://dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th - # ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991. - # - # 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794). - # In Proceedings of the 23rd Symposium on Principles of Programming Languages - # (PoPL), 1996. - class MVar < Synchronization::Object - include Concern::Dereferenceable - safe_initialization! - - # Unique value that represents that an `MVar` was empty - EMPTY = ::Object.new - - # Unique value that represents that an `MVar` timed out before it was able - # to produce a value. - TIMEOUT = ::Object.new - - # Create a new `MVar`, either empty or with an initial value. - # - # @param [Hash] opts the options controlling how the future will be processed - # - # @!macro deref_options - def initialize(value = EMPTY, opts = {}) - @value = value - @mutex = Mutex.new - @empty_condition = ConditionVariable.new - @full_condition = ConditionVariable.new - set_deref_options(opts) - end - - # Remove the value from an `MVar`, leaving it empty, and blocking if there - # isn't a value. A timeout can be set to limit the time spent blocked, in - # which case it returns `TIMEOUT` if the time is exceeded. - # @return [Object] the value that was taken, or `TIMEOUT` - def take(timeout = nil) - @mutex.synchronize do - wait_for_full(timeout) - - # If we timed out we'll still be empty - if unlocked_full? - value = @value - @value = EMPTY - @empty_condition.signal - apply_deref_options(value) - else - TIMEOUT - end - end - end - - # acquires lock on the from an `MVAR`, yields the value to provided block, - # and release lock. A timeout can be set to limit the time spent blocked, - # in which case it returns `TIMEOUT` if the time is exceeded. - # @return [Object] the value returned by the block, or `TIMEOUT` - def borrow(timeout = nil) - @mutex.synchronize do - wait_for_full(timeout) - - # if we timeoud out we'll still be empty - if unlocked_full? - yield @value - else - TIMEOUT - end - end - end - - # Put a value into an `MVar`, blocking if there is already a value until - # it is empty. A timeout can be set to limit the time spent blocked, in - # which case it returns `TIMEOUT` if the time is exceeded. - # @return [Object] the value that was put, or `TIMEOUT` - def put(value, timeout = nil) - @mutex.synchronize do - wait_for_empty(timeout) - - # If we timed out we won't be empty - if unlocked_empty? - @value = value - @full_condition.signal - apply_deref_options(value) - else - TIMEOUT - end - end - end - - # Atomically `take`, yield the value to a block for transformation, and then - # `put` the transformed value. Returns the transformed value. A timeout can - # be set to limit the time spent blocked, in which case it returns `TIMEOUT` - # if the time is exceeded. - # @return [Object] the transformed value, or `TIMEOUT` - def modify(timeout = nil) - raise ArgumentError.new('no block given') unless block_given? - - @mutex.synchronize do - wait_for_full(timeout) - - # If we timed out we'll still be empty - if unlocked_full? - value = @value - @value = yield value - @full_condition.signal - apply_deref_options(value) - else - TIMEOUT - end - end - end - - # Non-blocking version of `take`, that returns `EMPTY` instead of blocking. - def try_take! - @mutex.synchronize do - if unlocked_full? - value = @value - @value = EMPTY - @empty_condition.signal - apply_deref_options(value) - else - EMPTY - end - end - end - - # Non-blocking version of `put`, that returns whether or not it was successful. - def try_put!(value) - @mutex.synchronize do - if unlocked_empty? - @value = value - @full_condition.signal - true - else - false - end - end - end - - # Non-blocking version of `put` that will overwrite an existing value. - def set!(value) - @mutex.synchronize do - old_value = @value - @value = value - @full_condition.signal - apply_deref_options(old_value) - end - end - - # Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet. - def modify! - raise ArgumentError.new('no block given') unless block_given? - - @mutex.synchronize do - value = @value - @value = yield value - if unlocked_empty? - @empty_condition.signal - else - @full_condition.signal - end - apply_deref_options(value) - end - end - - # Returns if the `MVar` is currently empty. - def empty? - @mutex.synchronize { @value == EMPTY } - end - - # Returns if the `MVar` currently contains a value. - def full? - !empty? - end - - protected - - def synchronize(&block) - @mutex.synchronize(&block) - end - - private - - def unlocked_empty? - @value == EMPTY - end - - def unlocked_full? - ! unlocked_empty? - end - - def wait_for_full(timeout) - wait_while(@full_condition, timeout) { unlocked_empty? } - end - - def wait_for_empty(timeout) - wait_while(@empty_condition, timeout) { unlocked_full? } - end - - def wait_while(condition, timeout) - if timeout.nil? - while yield - condition.wait(@mutex) - end - else - stop = Concurrent.monotonic_time + timeout - while yield && timeout > 0.0 - condition.wait(@mutex, timeout) - timeout = stop - Concurrent.monotonic_time - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/options.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -require 'concurrent/configuration' - -module Concurrent - - # @!visibility private - module Options - - # Get the requested `Executor` based on the values set in the options hash. - # - # @param [Hash] opts the options defining the requested executor - # @option opts [Executor] :executor when set use the given `Executor` instance. - # Three special values are also supported: `:fast` returns the global fast executor, - # `:io` returns the global io executor, and `:immediate` returns a new - # `ImmediateExecutor` object. - # - # @return [Executor, nil] the requested thread pool, or nil when no option specified - # - # @!visibility private - def self.executor_from_options(opts = {}) # :nodoc: - if identifier = opts.fetch(:executor, nil) - executor(identifier) - else - nil - end - end - - def self.executor(executor_identifier) - case executor_identifier - when :fast - Concurrent.global_fast_executor - when :io - Concurrent.global_io_executor - when :immediate - Concurrent.global_immediate_executor - when Concurrent::ExecutorService - executor_identifier - else - raise ArgumentError, "executor not recognized by '#{executor_identifier}'" - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promise.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,579 +0,0 @@ -require 'thread' -require 'concurrent/constants' -require 'concurrent/errors' -require 'concurrent/ivar' -require 'concurrent/executor/safe_task_executor' - -require 'concurrent/options' - -module Concurrent - - PromiseExecutionError = Class.new(StandardError) - - # Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A) - # and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications. - # - # > A promise represents the eventual value returned from the single - # > completion of an operation. - # - # Promises are similar to futures and share many of the same behaviours. - # Promises are far more robust, however. Promises can be chained in a tree - # structure where each promise may have zero or more children. Promises are - # chained using the `then` method. The result of a call to `then` is always - # another promise. Promises are resolved asynchronously (with respect to the - # main thread) but in a strict order: parents are guaranteed to be resolved - # before their children, children before their younger siblings. The `then` - # method takes two parameters: an optional block to be executed upon parent - # resolution and an optional callable to be executed upon parent failure. The - # result of each promise is passed to each of its children upon resolution. - # When a promise is rejected all its children will be summarily rejected and - # will receive the reason. - # - # Promises have several possible states: *:unscheduled*, *:pending*, - # *:processing*, *:rejected*, or *:fulfilled*. These are also aggregated as - # `#incomplete?` and `#complete?`. When a Promise is created it is set to - # *:unscheduled*. Once the `#execute` method is called the state becomes - # *:pending*. Once a job is pulled from the thread pool's queue and is given - # to a thread for processing (often immediately upon `#post`) the state - # becomes *:processing*. The future will remain in this state until processing - # is complete. A future that is in the *:unscheduled*, *:pending*, or - # *:processing* is considered `#incomplete?`. A `#complete?` Promise is either - # *:rejected*, indicating that an exception was thrown during processing, or - # *:fulfilled*, indicating success. If a Promise is *:fulfilled* its `#value` - # will be updated to reflect the result of the operation. If *:rejected* the - # `reason` will be updated with a reference to the thrown exception. The - # predicate methods `#unscheduled?`, `#pending?`, `#rejected?`, and - # `#fulfilled?` can be called at any time to obtain the state of the Promise, - # as can the `#state` method, which returns a symbol. - # - # Retrieving the value of a promise is done through the `value` (alias: - # `deref`) method. Obtaining the value of a promise is a potentially blocking - # operation. When a promise is *rejected* a call to `value` will return `nil` - # immediately. When a promise is *fulfilled* a call to `value` will - # immediately return the current value. When a promise is *pending* a call to - # `value` will block until the promise is either *rejected* or *fulfilled*. A - # *timeout* value can be passed to `value` to limit how long the call will - # block. If `nil` the call will block indefinitely. If `0` the call will not - # block. Any other integer or float value will indicate the maximum number of - # seconds to block. - # - # Promises run on the global thread pool. - # - # @!macro copy_options - # - # ### Examples - # - # Start by requiring promises - # - # ```ruby - # require 'concurrent' - # ``` - # - # Then create one - # - # ```ruby - # p = Concurrent::Promise.execute do - # # do something - # 42 - # end - # ``` - # - # Promises can be chained using the `then` method. The `then` method accepts a - # block and an executor, to be executed on fulfillment, and a callable argument to be executed - # on rejection. The result of the each promise is passed as the block argument - # to chained promises. - # - # ```ruby - # p = Concurrent::Promise.new{10}.then{|x| x * 2}.then{|result| result - 10 }.execute - # ``` - # - # And so on, and so on, and so on... - # - # ```ruby - # p = Concurrent::Promise.fulfill(20). - # then{|result| result - 10 }. - # then{|result| result * 3 }. - # then(executor: different_executor){|result| result % 5 }.execute - # ``` - # - # The initial state of a newly created Promise depends on the state of its parent: - # - if parent is *unscheduled* the child will be *unscheduled* - # - if parent is *pending* the child will be *pending* - # - if parent is *fulfilled* the child will be *pending* - # - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*) - # - # Promises are executed asynchronously from the main thread. By the time a - # child Promise finishes intialization it may be in a different state than its - # parent (by the time a child is created its parent may have completed - # execution and changed state). Despite being asynchronous, however, the order - # of execution of Promise objects in a chain (or tree) is strictly defined. - # - # There are multiple ways to create and execute a new `Promise`. Both ways - # provide identical behavior: - # - # ```ruby - # # create, operate, then execute - # p1 = Concurrent::Promise.new{ "Hello World!" } - # p1.state #=> :unscheduled - # p1.execute - # - # # create and immediately execute - # p2 = Concurrent::Promise.new{ "Hello World!" }.execute - # - # # execute during creation - # p3 = Concurrent::Promise.execute{ "Hello World!" } - # ``` - # - # Once the `execute` method is called a `Promise` becomes `pending`: - # - # ```ruby - # p = Concurrent::Promise.execute{ "Hello, world!" } - # p.state #=> :pending - # p.pending? #=> true - # ``` - # - # Wait a little bit, and the promise will resolve and provide a value: - # - # ```ruby - # p = Concurrent::Promise.execute{ "Hello, world!" } - # sleep(0.1) - # - # p.state #=> :fulfilled - # p.fulfilled? #=> true - # p.value #=> "Hello, world!" - # ``` - # - # If an exception occurs, the promise will be rejected and will provide - # a reason for the rejection: - # - # ```ruby - # p = Concurrent::Promise.execute{ raise StandardError.new("Here comes the Boom!") } - # sleep(0.1) - # - # p.state #=> :rejected - # p.rejected? #=> true - # p.reason #=> "#" - # ``` - # - # #### Rejection - # - # When a promise is rejected all its children will be rejected and will - # receive the rejection `reason` as the rejection callable parameter: - # - # ```ruby - # p = Concurrent::Promise.execute { Thread.pass; raise StandardError } - # - # c1 = p.then(-> reason { 42 }) - # c2 = p.then(-> reason { raise 'Boom!' }) - # - # c1.wait.state #=> :fulfilled - # c1.value #=> 45 - # c2.wait.state #=> :rejected - # c2.reason #=> # - # ``` - # - # Once a promise is rejected it will continue to accept children that will - # receive immediately rejection (they will be executed asynchronously). - # - # #### Aliases - # - # The `then` method is the most generic alias: it accepts a block to be - # executed upon parent fulfillment and a callable to be executed upon parent - # rejection. At least one of them should be passed. The default block is `{ - # |result| result }` that fulfills the child with the parent value. The - # default callable is `{ |reason| raise reason }` that rejects the child with - # the parent reason. - # - # - `on_success { |result| ... }` is the same as `then {|result| ... }` - # - `rescue { |reason| ... }` is the same as `then(Proc.new { |reason| ... } )` - # - `rescue` is aliased by `catch` and `on_error` - class Promise < IVar - - # Initialize a new Promise with the provided options. - # - # @!macro executor_and_deref_options - # - # @!macro promise_init_options - # - # @option opts [Promise] :parent the parent `Promise` when building a chain/tree - # @option opts [Proc] :on_fulfill fulfillment handler - # @option opts [Proc] :on_reject rejection handler - # @option opts [object, Array] :args zero or more arguments to be passed - # the task block on execution - # - # @yield The block operation to be performed asynchronously. - # - # @raise [ArgumentError] if no block is given - # - # @see http://wiki.commonjs.org/wiki/Promises/A - # @see http://promises-aplus.github.io/promises-spec/ - def initialize(opts = {}, &block) - opts.delete_if { |k, v| v.nil? } - super(NULL, opts.merge(__promise_body_from_block__: block), &nil) - end - - # Create a new `Promise` and fulfill it immediately. - # - # @!macro executor_and_deref_options - # - # @!macro promise_init_options - # - # @raise [ArgumentError] if no block is given - # - # @return [Promise] the newly created `Promise` - def self.fulfill(value, opts = {}) - Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) } - end - - # Create a new `Promise` and reject it immediately. - # - # @!macro executor_and_deref_options - # - # @!macro promise_init_options - # - # @raise [ArgumentError] if no block is given - # - # @return [Promise] the newly created `Promise` - def self.reject(reason, opts = {}) - Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) } - end - - # Execute an `:unscheduled` `Promise`. Immediately sets the state to `:pending` and - # passes the block to a new thread/thread pool for eventual execution. - # Does nothing if the `Promise` is in any state other than `:unscheduled`. - # - # @return [Promise] a reference to `self` - def execute - if root? - if compare_and_set_state(:pending, :unscheduled) - set_pending - realize(@promise_body) - end - else - @parent.execute - end - self - end - - # @!macro ivar_set_method - # - # @raise [Concurrent::PromiseExecutionError] if not the root promise - def set(value = NULL, &block) - raise PromiseExecutionError.new('supported only on root promise') unless root? - check_for_block_or_value!(block_given?, value) - synchronize do - if @state != :unscheduled - raise MultipleAssignmentError - else - @promise_body = block || Proc.new { |result| value } - end - end - execute - end - - # @!macro ivar_fail_method - # - # @raise [Concurrent::PromiseExecutionError] if not the root promise - def fail(reason = StandardError.new) - set { raise reason } - end - - # Create a new `Promise` object with the given block, execute it, and return the - # `:pending` object. - # - # @!macro executor_and_deref_options - # - # @!macro promise_init_options - # - # @return [Promise] the newly created `Promise` in the `:pending` state - # - # @raise [ArgumentError] if no block is given - # - # @example - # promise = Concurrent::Promise.execute{ sleep(1); 42 } - # promise.state #=> :pending - def self.execute(opts = {}, &block) - new(opts, &block).execute - end - - # Chain a new promise off the current promise. - # - # @return [Promise] the new promise - # @yield The block operation to be performed asynchronously. - # @overload then(rescuer, executor, &block) - # @param [Proc] rescuer An optional rescue block to be executed if the - # promise is rejected. - # @param [ThreadPool] executor An optional thread pool executor to be used - # in the new Promise - # @overload then(rescuer, executor: executor, &block) - # @param [Proc] rescuer An optional rescue block to be executed if the - # promise is rejected. - # @param [ThreadPool] executor An optional thread pool executor to be used - # in the new Promise - def then(*args, &block) - if args.last.is_a?(::Hash) - executor = args.pop[:executor] - rescuer = args.first - else - rescuer, executor = args - end - - executor ||= @executor - - raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given? - block = Proc.new { |result| result } unless block_given? - child = Promise.new( - parent: self, - executor: executor, - on_fulfill: block, - on_reject: rescuer - ) - - synchronize do - child.state = :pending if @state == :pending - child.on_fulfill(apply_deref_options(@value)) if @state == :fulfilled - child.on_reject(@reason) if @state == :rejected - @children << child - end - - child - end - - # Chain onto this promise an action to be undertaken on success - # (fulfillment). - # - # @yield The block to execute - # - # @return [Promise] self - def on_success(&block) - raise ArgumentError.new('no block given') unless block_given? - self.then(&block) - end - - # Chain onto this promise an action to be undertaken on failure - # (rejection). - # - # @yield The block to execute - # - # @return [Promise] self - def rescue(&block) - self.then(block) - end - - alias_method :catch, :rescue - alias_method :on_error, :rescue - - # Yield the successful result to the block that returns a promise. If that - # promise is also successful the result is the result of the yielded promise. - # If either part fails the whole also fails. - # - # @example - # Promise.execute { 1 }.flat_map { |v| Promise.execute { v + 2 } }.value! #=> 3 - # - # @return [Promise] - def flat_map(&block) - child = Promise.new( - parent: self, - executor: ImmediateExecutor.new, - ) - - on_error { |e| child.on_reject(e) } - on_success do |result1| - begin - inner = block.call(result1) - inner.execute - inner.on_success { |result2| child.on_fulfill(result2) } - inner.on_error { |e| child.on_reject(e) } - rescue => e - child.on_reject(e) - end - end - - child - end - - # Builds a promise that produces the result of promises in an Array - # and fails if any of them fails. - # - # @overload zip(*promises) - # @param [Array] promises - # - # @overload zip(*promises, opts) - # @param [Array] promises - # @param [Hash] opts the configuration options - # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance. - # @option opts [Boolean] :execute (true) execute promise before returning - # - # @return [Promise] - def self.zip(*promises) - opts = promises.last.is_a?(::Hash) ? promises.pop.dup : {} - opts[:executor] ||= ImmediateExecutor.new - zero = if !opts.key?(:execute) || opts.delete(:execute) - fulfill([], opts) - else - Promise.new(opts) { [] } - end - - promises.reduce(zero) do |p1, p2| - p1.flat_map do |results| - p2.then do |next_result| - results << next_result - end - end - end - end - - # Builds a promise that produces the result of self and others in an Array - # and fails if any of them fails. - # - # @overload zip(*promises) - # @param [Array] others - # - # @overload zip(*promises, opts) - # @param [Array] others - # @param [Hash] opts the configuration options - # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance. - # @option opts [Boolean] :execute (true) execute promise before returning - # - # @return [Promise] - def zip(*others) - self.class.zip(self, *others) - end - - # Aggregates a collection of promises and executes the `then` condition - # if all aggregated promises succeed. Executes the `rescue` handler with - # a `Concurrent::PromiseExecutionError` if any of the aggregated promises - # fail. Upon execution will execute any of the aggregate promises that - # were not already executed. - # - # @!macro promise_self_aggregate - # - # The returned promise will not yet have been executed. Additional `#then` - # and `#rescue` handlers may still be provided. Once the returned promise - # is execute the aggregate promises will be also be executed (if they have - # not been executed already). The results of the aggregate promises will - # be checked upon completion. The necessary `#then` and `#rescue` blocks - # on the aggregating promise will then be executed as appropriate. If the - # `#rescue` handlers are executed the raises exception will be - # `Concurrent::PromiseExecutionError`. - # - # @param [Array] promises Zero or more promises to aggregate - # @return [Promise] an unscheduled (not executed) promise that aggregates - # the promises given as arguments - def self.all?(*promises) - aggregate(:all?, *promises) - end - - # Aggregates a collection of promises and executes the `then` condition - # if any aggregated promises succeed. Executes the `rescue` handler with - # a `Concurrent::PromiseExecutionError` if any of the aggregated promises - # fail. Upon execution will execute any of the aggregate promises that - # were not already executed. - # - # @!macro promise_self_aggregate - def self.any?(*promises) - aggregate(:any?, *promises) - end - - protected - - def ns_initialize(value, opts) - super - - @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor - @args = get_arguments_from(opts) - - @parent = opts.fetch(:parent) { nil } - @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } } - @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } } - - @promise_body = opts[:__promise_body_from_block__] || Proc.new { |result| result } - @state = :unscheduled - @children = [] - end - - # Aggregate a collection of zero or more promises under a composite promise, - # execute the aggregated promises and collect them into a standard Ruby array, - # call the given Ruby `Ennnumerable` predicate (such as `any?`, `all?`, `none?`, - # or `one?`) on the collection checking for the success or failure of each, - # then executing the composite's `#then` handlers if the predicate returns - # `true` or executing the composite's `#rescue` handlers if the predicate - # returns false. - # - # @!macro promise_self_aggregate - def self.aggregate(method, *promises) - composite = Promise.new do - completed = promises.collect do |promise| - promise.execute if promise.unscheduled? - promise.wait - promise - end - unless completed.empty? || completed.send(method){|promise| promise.fulfilled? } - raise PromiseExecutionError - end - end - composite - end - - # @!visibility private - def set_pending - synchronize do - @state = :pending - @children.each { |c| c.set_pending } - end - end - - # @!visibility private - def root? # :nodoc: - @parent.nil? - end - - # @!visibility private - def on_fulfill(result) - realize Proc.new { @on_fulfill.call(result) } - nil - end - - # @!visibility private - def on_reject(reason) - realize Proc.new { @on_reject.call(reason) } - nil - end - - # @!visibility private - def notify_child(child) - if_state(:fulfilled) { child.on_fulfill(apply_deref_options(@value)) } - if_state(:rejected) { child.on_reject(@reason) } - end - - # @!visibility private - def complete(success, value, reason) - children_to_notify = synchronize do - set_state!(success, value, reason) - @children.dup - end - - children_to_notify.each { |child| notify_child(child) } - observers.notify_and_delete_observers{ [Time.now, self.value, reason] } - end - - # @!visibility private - def realize(task) - @executor.post do - success, value, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args) - complete(success, value, reason) - end - end - - # @!visibility private - def set_state!(success, value, reason) - set_state(success, value, reason) - event.set - end - - # @!visibility private - def synchronized_set_state!(success, value, reason) - synchronize { set_state!(success, value, reason) } - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/promises.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,2167 +0,0 @@ -require 'concurrent/synchronization' -require 'concurrent/atomic/atomic_boolean' -require 'concurrent/atomic/atomic_fixnum' -require 'concurrent/collection/lock_free_stack' -require 'concurrent/errors' -require 'concurrent/re_include' - -module Concurrent - - # {include:file:docs-source/promises-main.md} - module Promises - - # @!macro promises.param.default_executor - # @param [Executor, :io, :fast] default_executor Instance of an executor or a name of the - # global executor. Default executor propagates to chained futures unless overridden with - # executor parameter or changed with {AbstractEventFuture#with_default_executor}. - # - # @!macro promises.param.executor - # @param [Executor, :io, :fast] executor Instance of an executor or a name of the - # global executor. The task is executed on it, default executor remains unchanged. - # - # @!macro promises.param.args - # @param [Object] args arguments which are passed to the task when it's executed. - # (It might be prepended with other arguments, see the @yeild section). - # - # @!macro promises.shortcut.on - # Shortcut of {#$0_on} with default `:io` executor supplied. - # @see #$0_on - # - # @!macro promises.shortcut.using - # Shortcut of {#$0_using} with default `:io` executor supplied. - # @see #$0_using - # - # @!macro promise.param.task-future - # @yieldreturn will become result of the returned Future. - # Its returned value becomes {Future#value} fulfilling it, - # raised exception becomes {Future#reason} rejecting it. - # - # @!macro promise.param.callback - # @yieldreturn is forgotten. - - # Container of all {Future}, {Event} factory methods. They are never constructed directly with - # new. - module FactoryMethods - extend ReInclude - extend self - - module Configuration - # @return [Executor, :io, :fast] the executor which is used when none is supplied - # to a factory method. The method can be overridden in the receivers of - # `include FactoryMethod` - def default_executor - :io - end - end - - include Configuration - - # @!macro promises.shortcut.on - # @return [ResolvableEvent] - def resolvable_event - resolvable_event_on default_executor - end - - # Created resolvable event, user is responsible for resolving the event once by - # {Promises::ResolvableEvent#resolve}. - # - # @!macro promises.param.default_executor - # @return [ResolvableEvent] - def resolvable_event_on(default_executor = self.default_executor) - ResolvableEventPromise.new(default_executor).future - end - - # @!macro promises.shortcut.on - # @return [ResolvableFuture] - def resolvable_future - resolvable_future_on default_executor - end - - # Creates resolvable future, user is responsible for resolving the future once by - # {Promises::ResolvableFuture#resolve}, {Promises::ResolvableFuture#fulfill}, - # or {Promises::ResolvableFuture#reject} - # - # @!macro promises.param.default_executor - # @return [ResolvableFuture] - def resolvable_future_on(default_executor = self.default_executor) - ResolvableFuturePromise.new(default_executor).future - end - - # @!macro promises.shortcut.on - # @return [Future] - def future(*args, &task) - future_on(default_executor, *args, &task) - end - - # Constructs new Future which will be resolved after block is evaluated on default executor. - # Evaluation begins immediately. - # - # @!macro promises.param.default_executor - # @!macro promises.param.args - # @yield [*args] to the task. - # @!macro promise.param.task-future - # @return [Future] - def future_on(default_executor, *args, &task) - ImmediateEventPromise.new(default_executor).future.then(*args, &task) - end - - # Creates resolved future with will be either fulfilled with the given value or rejection with - # the given reason. - # - # @param [true, false] fulfilled - # @param [Object] value - # @param [Object] reason - # @!macro promises.param.default_executor - # @return [Future] - def resolved_future(fulfilled, value, reason, default_executor = self.default_executor) - ImmediateFuturePromise.new(default_executor, fulfilled, value, reason).future - end - - # Creates resolved future with will be fulfilled with the given value. - # - # @!macro promises.param.default_executor - # @param [Object] value - # @return [Future] - def fulfilled_future(value, default_executor = self.default_executor) - resolved_future true, value, nil, default_executor - end - - # Creates resolved future with will be rejected with the given reason. - # - # @!macro promises.param.default_executor - # @param [Object] reason - # @return [Future] - def rejected_future(reason, default_executor = self.default_executor) - resolved_future false, nil, reason, default_executor - end - - # Creates resolved event. - # - # @!macro promises.param.default_executor - # @return [Event] - def resolved_event(default_executor = self.default_executor) - ImmediateEventPromise.new(default_executor).event - end - - # General constructor. Behaves differently based on the argument's type. It's provided for convenience - # but it's better to be explicit. - # - # @see rejected_future, resolved_event, fulfilled_future - # @!macro promises.param.default_executor - # @return [Event, Future] - # - # @overload make_future(nil, default_executor = self.default_executor) - # @param [nil] nil - # @return [Event] resolved event. - # - # @overload make_future(a_future, default_executor = self.default_executor) - # @param [Future] a_future - # @return [Future] a future which will be resolved when a_future is. - # - # @overload make_future(an_event, default_executor = self.default_executor) - # @param [Event] an_event - # @return [Event] an event which will be resolved when an_event is. - # - # @overload make_future(exception, default_executor = self.default_executor) - # @param [Exception] exception - # @return [Future] a rejected future with the exception as its reason. - # - # @overload make_future(value, default_executor = self.default_executor) - # @param [Object] value when none of the above overloads fits - # @return [Future] a fulfilled future with the value. - def make_future(argument = nil, default_executor = self.default_executor) - case argument - when AbstractEventFuture - # returning wrapper would change nothing - argument - when Exception - rejected_future argument, default_executor - when nil - resolved_event default_executor - else - fulfilled_future argument, default_executor - end - end - - # @!macro promises.shortcut.on - # @return [Future, Event] - def delay(*args, &task) - delay_on default_executor, *args, &task - end - - # Creates new event or future which is resolved only after it is touched, - # see {Concurrent::AbstractEventFuture#touch}. - # - # @!macro promises.param.default_executor - # @overload delay_on(default_executor, *args, &task) - # If task is provided it returns a {Future} representing the result of the task. - # @!macro promises.param.args - # @yield [*args] to the task. - # @!macro promise.param.task-future - # @return [Future] - # @overload delay_on(default_executor) - # If no task is provided, it returns an {Event} - # @return [Event] - def delay_on(default_executor, *args, &task) - event = DelayPromise.new(default_executor).event - task ? event.chain(*args, &task) : event - end - - # @!macro promises.shortcut.on - # @return [Future, Event] - def schedule(intended_time, *args, &task) - schedule_on default_executor, intended_time, *args, &task - end - - # Creates new event or future which is resolved in intended_time. - # - # @!macro promises.param.default_executor - # @!macro promises.param.intended_time - # @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds. - # `Time` means to run on `intended_time`. - # @overload schedule_on(default_executor, intended_time, *args, &task) - # If task is provided it returns a {Future} representing the result of the task. - # @!macro promises.param.args - # @yield [*args] to the task. - # @!macro promise.param.task-future - # @return [Future] - # @overload schedule_on(default_executor, intended_time) - # If no task is provided, it returns an {Event} - # @return [Event] - def schedule_on(default_executor, intended_time, *args, &task) - event = ScheduledPromise.new(default_executor, intended_time).event - task ? event.chain(*args, &task) : event - end - - # @!macro promises.shortcut.on - # @return [Future] - def zip_futures(*futures_and_or_events) - zip_futures_on default_executor, *futures_and_or_events - end - - # Creates new future which is resolved after all futures_and_or_events are resolved. - # Its value is array of zipped future values. Its reason is array of reasons for rejection. - # If there is an error it rejects. - # @!macro promises.event-conversion - # If event is supplied, which does not have value and can be only resolved, it's - # represented as `:fulfilled` with value `nil`. - # - # @!macro promises.param.default_executor - # @param [AbstractEventFuture] futures_and_or_events - # @return [Future] - def zip_futures_on(default_executor, *futures_and_or_events) - ZipFuturesPromise.new_blocked_by(futures_and_or_events, default_executor).future - end - - alias_method :zip, :zip_futures - - # @!macro promises.shortcut.on - # @return [Event] - def zip_events(*futures_and_or_events) - zip_events_on default_executor, *futures_and_or_events - end - - # Creates new event which is resolved after all futures_and_or_events are resolved. - # (Future is resolved when fulfilled or rejected.) - # - # @!macro promises.param.default_executor - # @param [AbstractEventFuture] futures_and_or_events - # @return [Event] - def zip_events_on(default_executor, *futures_and_or_events) - ZipEventsPromise.new_blocked_by(futures_and_or_events, default_executor).event - end - - # @!macro promises.shortcut.on - # @return [Future] - def any_resolved_future(*futures_and_or_events) - any_resolved_future_on default_executor, *futures_and_or_events - end - - alias_method :any, :any_resolved_future - - # Creates new future which is resolved after first futures_and_or_events is resolved. - # Its result equals result of the first resolved future. - # @!macro promises.any-touch - # If resolved it does not propagate {Concurrent::AbstractEventFuture#touch}, leaving delayed - # futures un-executed if they are not required any more. - # @!macro promises.event-conversion - # - # @!macro promises.param.default_executor - # @param [AbstractEventFuture] futures_and_or_events - # @return [Future] - def any_resolved_future_on(default_executor, *futures_and_or_events) - AnyResolvedFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future - end - - # @!macro promises.shortcut.on - # @return [Future] - def any_fulfilled_future(*futures_and_or_events) - any_fulfilled_future_on default_executor, *futures_and_or_events - end - - # Creates new future which is resolved after first of futures_and_or_events is fulfilled. - # Its result equals result of the first resolved future or if all futures_and_or_events reject, - # it has reason of the last resolved future. - # @!macro promises.any-touch - # @!macro promises.event-conversion - # - # @!macro promises.param.default_executor - # @param [AbstractEventFuture] futures_and_or_events - # @return [Future] - def any_fulfilled_future_on(default_executor, *futures_and_or_events) - AnyFulfilledFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future - end - - # @!macro promises.shortcut.on - # @return [Future] - def any_event(*futures_and_or_events) - any_event_on default_executor, *futures_and_or_events - end - - # Creates new event which becomes resolved after first of the futures_and_or_events resolves. - # @!macro promises.any-touch - # - # @!macro promises.param.default_executor - # @param [AbstractEventFuture] futures_and_or_events - # @return [Event] - def any_event_on(default_executor, *futures_and_or_events) - AnyResolvedEventPromise.new_blocked_by(futures_and_or_events, default_executor).event - end - - # TODO consider adding first(count, *futures) - # TODO consider adding zip_by(slice, *futures) processing futures in slices - # TODO or rather a generic aggregator taking a function - end - - module InternalStates - # @!visibility private - class State - def resolved? - raise NotImplementedError - end - - def to_sym - raise NotImplementedError - end - end - - # @!visibility private - class Pending < State - def resolved? - false - end - - def to_sym - :pending - end - end - - # @!visibility private - class Reserved < Pending - end - - # @!visibility private - class ResolvedWithResult < State - def resolved? - true - end - - def to_sym - :resolved - end - - def result - [fulfilled?, value, reason] - end - - def fulfilled? - raise NotImplementedError - end - - def value - raise NotImplementedError - end - - def reason - raise NotImplementedError - end - - def apply - raise NotImplementedError - end - end - - # @!visibility private - class Fulfilled < ResolvedWithResult - - def initialize(value) - @Value = value - end - - def fulfilled? - true - end - - def apply(args, block) - block.call value, *args - end - - def value - @Value - end - - def reason - nil - end - - def to_sym - :fulfilled - end - end - - # @!visibility private - class FulfilledArray < Fulfilled - def apply(args, block) - block.call(*value, *args) - end - end - - # @!visibility private - class Rejected < ResolvedWithResult - def initialize(reason) - @Reason = reason - end - - def fulfilled? - false - end - - def value - nil - end - - def reason - @Reason - end - - def to_sym - :rejected - end - - def apply(args, block) - block.call reason, *args - end - end - - # @!visibility private - class PartiallyRejected < ResolvedWithResult - def initialize(value, reason) - super() - @Value = value - @Reason = reason - end - - def fulfilled? - false - end - - def to_sym - :rejected - end - - def value - @Value - end - - def reason - @Reason - end - - def apply(args, block) - block.call(*reason, *args) - end - end - - # @!visibility private - PENDING = Pending.new - # @!visibility private - RESERVED = Reserved.new - # @!visibility private - RESOLVED = Fulfilled.new(nil) - - def RESOLVED.to_sym - :resolved - end - end - - private_constant :InternalStates - - # @!macro promises.shortcut.event-future - # @see Event#$0 - # @see Future#$0 - - # @!macro promises.param.timeout - # @param [Numeric] timeout the maximum time in second to wait. - - # @!macro promises.warn.blocks - # @note This function potentially blocks current thread until the Future is resolved. - # Be careful it can deadlock. Try to chain instead. - - # Common ancestor of {Event} and {Future} classes, many shared methods are defined here. - class AbstractEventFuture < Synchronization::Object - safe_initialization! - attr_atomic(:internal_state) - private :internal_state=, :swap_internal_state, :compare_and_set_internal_state, :update_internal_state - # @!method internal_state - # @!visibility private - - include InternalStates - - def initialize(promise, default_executor) - super() - @Lock = Mutex.new - @Condition = ConditionVariable.new - @Promise = promise - @DefaultExecutor = default_executor - @Callbacks = LockFreeStack.new - @Waiters = AtomicFixnum.new 0 - self.internal_state = PENDING - end - - private :initialize - - # Returns its state. - # @return [Symbol] - # - # @overload an_event.state - # @return [:pending, :resolved] - # @overload a_future.state - # Both :fulfilled, :rejected implies :resolved. - # @return [:pending, :fulfilled, :rejected] - def state - internal_state.to_sym - end - - # Is it in pending state? - # @return [Boolean] - def pending? - !internal_state.resolved? - end - - # Is it in resolved state? - # @return [Boolean] - def resolved? - internal_state.resolved? - end - - # Propagates touch. Requests all the delayed futures, which it depends on, to be - # executed. This method is called by any other method requiring resolved state, like {#wait}. - # @return [self] - def touch - @Promise.touch - self - end - - # @!macro promises.touches - # Calls {Concurrent::AbstractEventFuture#touch}. - - # @!macro promises.method.wait - # Wait (block the Thread) until receiver is {#resolved?}. - # @!macro promises.touches - # - # @!macro promises.warn.blocks - # @!macro promises.param.timeout - # @return [self, true, false] self implies timeout was not used, true implies timeout was used - # and it was resolved, false implies it was not resolved within timeout. - def wait(timeout = nil) - result = wait_until_resolved(timeout) - timeout ? result : self - end - - # Returns default executor. - # @return [Executor] default executor - # @see #with_default_executor - # @see FactoryMethods#future_on - # @see FactoryMethods#resolvable_future - # @see FactoryMethods#any_fulfilled_future_on - # @see similar - def default_executor - @DefaultExecutor - end - - # @!macro promises.shortcut.on - # @return [Future] - def chain(*args, &task) - chain_on @DefaultExecutor, *args, &task - end - - # Chains the task to be executed asynchronously on executor after it is resolved. - # - # @!macro promises.param.executor - # @!macro promises.param.args - # @return [Future] - # @!macro promise.param.task-future - # - # @overload an_event.chain_on(executor, *args, &task) - # @yield [*args] to the task. - # @overload a_future.chain_on(executor, *args, &task) - # @yield [fulfilled, value, reason, *args] to the task. - # @yieldparam [true, false] fulfilled - # @yieldparam [Object] value - # @yieldparam [Object] reason - def chain_on(executor, *args, &task) - ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future - end - - # @return [String] Short string representation. - def to_s - format '%s %s>', super[0..-2], state - end - - alias_method :inspect, :to_s - - # Resolves the resolvable when receiver is resolved. - # - # @param [Resolvable] resolvable - # @return [self] - def chain_resolvable(resolvable) - on_resolution! { resolvable.resolve_with internal_state } - end - - alias_method :tangle, :chain_resolvable - - # @!macro promises.shortcut.using - # @return [self] - def on_resolution(*args, &callback) - on_resolution_using @DefaultExecutor, *args, &callback - end - - # Stores the callback to be executed synchronously on resolving thread after it is - # resolved. - # - # @!macro promises.param.args - # @!macro promise.param.callback - # @return [self] - # - # @overload an_event.on_resolution!(*args, &callback) - # @yield [*args] to the callback. - # @overload a_future.on_resolution!(*args, &callback) - # @yield [fulfilled, value, reason, *args] to the callback. - # @yieldparam [true, false] fulfilled - # @yieldparam [Object] value - # @yieldparam [Object] reason - def on_resolution!(*args, &callback) - add_callback :callback_on_resolution, args, callback - end - - # Stores the callback to be executed asynchronously on executor after it is resolved. - # - # @!macro promises.param.executor - # @!macro promises.param.args - # @!macro promise.param.callback - # @return [self] - # - # @overload an_event.on_resolution_using(executor, *args, &callback) - # @yield [*args] to the callback. - # @overload a_future.on_resolution_using(executor, *args, &callback) - # @yield [fulfilled, value, reason, *args] to the callback. - # @yieldparam [true, false] fulfilled - # @yieldparam [Object] value - # @yieldparam [Object] reason - def on_resolution_using(executor, *args, &callback) - add_callback :async_callback_on_resolution, executor, args, callback - end - - # @!macro promises.method.with_default_executor - # Crates new object with same class with the executor set as its new default executor. - # Any futures depending on it will use the new default executor. - # @!macro promises.shortcut.event-future - # @abstract - # @return [AbstractEventFuture] - def with_default_executor(executor) - raise NotImplementedError - end - - # @!visibility private - def resolve_with(state, raise_on_reassign = true, reserved = false) - if compare_and_set_internal_state(reserved ? RESERVED : PENDING, state) - # go to synchronized block only if there were waiting threads - @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0 - call_callbacks state - else - return rejected_resolution(raise_on_reassign, state) - end - self - end - - # For inspection. - # @!visibility private - # @return [Array] - def blocks - @Callbacks.each_with_object([]) do |(method, args), promises| - promises.push(args[0]) if method == :callback_notify_blocked - end - end - - # For inspection. - # @!visibility private - def callbacks - @Callbacks.each.to_a - end - - # For inspection. - # @!visibility private - def promise - @Promise - end - - # For inspection. - # @!visibility private - def touched? - promise.touched? - end - - # For inspection. - # @!visibility private - def waiting_threads - @Waiters.each.to_a - end - - # @!visibility private - def add_callback_notify_blocked(promise, index) - add_callback :callback_notify_blocked, promise, index - end - - # @!visibility private - def add_callback_clear_delayed_node(node) - add_callback(:callback_clear_delayed_node, node) - end - - # @!visibility private - def with_hidden_resolvable - # TODO (pitr-ch 10-Dec-2018): documentation, better name if in edge - self - end - - private - - def add_callback(method, *args) - state = internal_state - if state.resolved? - call_callback method, state, args - else - @Callbacks.push [method, args] - state = internal_state - # take back if it was resolved in the meanwhile - call_callbacks state if state.resolved? - end - self - end - - def callback_clear_delayed_node(state, node) - node.value = nil - end - - # @return [Boolean] - def wait_until_resolved(timeout) - return true if resolved? - - touch - - @Lock.synchronize do - @Waiters.increment - begin - unless resolved? - @Condition.wait @Lock, timeout - end - ensure - # JRuby may raise ConcurrencyError - @Waiters.decrement - end - end - resolved? - end - - def call_callback(method, state, args) - self.send method, state, *args - end - - def call_callbacks(state) - method, args = @Callbacks.pop - while method - call_callback method, state, args - method, args = @Callbacks.pop - end - end - - def with_async(executor, *args, &block) - Concurrent.executor(executor).post(*args, &block) - end - - def async_callback_on_resolution(state, executor, args, callback) - with_async(executor, state, args, callback) do |st, ar, cb| - callback_on_resolution st, ar, cb - end - end - - def callback_notify_blocked(state, promise, index) - promise.on_blocker_resolution self, index - end - end - - # Represents an event which will happen in future (will be resolved). The event is either - # pending or resolved. It should be always resolved. Use {Future} to communicate rejections and - # cancellation. - class Event < AbstractEventFuture - - alias_method :then, :chain - - - # @!macro promises.method.zip - # Creates a new event or a future which will be resolved when receiver and other are. - # Returns an event if receiver and other are events, otherwise returns a future. - # If just one of the parties is Future then the result - # of the returned future is equal to the result of the supplied future. If both are futures - # then the result is as described in {FactoryMethods#zip_futures_on}. - # - # @return [Future, Event] - def zip(other) - if other.is_a?(Future) - ZipFutureEventPromise.new_blocked_by2(other, self, @DefaultExecutor).future - else - ZipEventEventPromise.new_blocked_by2(self, other, @DefaultExecutor).event - end - end - - alias_method :&, :zip - - # Creates a new event which will be resolved when the first of receiver, `event_or_future` - # resolves. - # - # @return [Event] - def any(event_or_future) - AnyResolvedEventPromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).event - end - - alias_method :|, :any - - # Creates new event dependent on receiver which will not evaluate until touched, see {#touch}. - # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated. - # - # @return [Event] - def delay - event = DelayPromise.new(@DefaultExecutor).event - ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event - end - - # @!macro promise.method.schedule - # Creates new event dependent on receiver scheduled to execute on/in intended_time. - # In time is interpreted from the moment the receiver is resolved, therefore it inserts - # delay into the chain. - # - # @!macro promises.param.intended_time - # @return [Event] - def schedule(intended_time) - chain do - event = ScheduledPromise.new(@DefaultExecutor, intended_time).event - ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event - end.flat_event - end - - # Converts event to a future. The future is fulfilled when the event is resolved, the future may never fail. - # - # @return [Future] - def to_future - future = Promises.resolvable_future - ensure - chain_resolvable(future) - end - - # Returns self, since this is event - # @return [Event] - def to_event - self - end - - # @!macro promises.method.with_default_executor - # @return [Event] - def with_default_executor(executor) - EventWrapperPromise.new_blocked_by1(self, executor).event - end - - private - - def rejected_resolution(raise_on_reassign, state) - Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign - return false - end - - def callback_on_resolution(state, args, callback) - callback.call(*args) - end - end - - # Represents a value which will become available in future. May reject with a reason instead, - # e.g. when the tasks raises an exception. - class Future < AbstractEventFuture - - # Is it in fulfilled state? - # @return [Boolean] - def fulfilled? - state = internal_state - state.resolved? && state.fulfilled? - end - - # Is it in rejected state? - # @return [Boolean] - def rejected? - state = internal_state - state.resolved? && !state.fulfilled? - end - - # @!macro promises.warn.nil - # @note Make sure returned `nil` is not confused with timeout, no value when rejected, - # no reason when fulfilled, etc. - # Use more exact methods if needed, like {#wait}, {#value!}, {#result}, etc. - - # @!macro promises.method.value - # Return value of the future. - # @!macro promises.touches - # - # @!macro promises.warn.blocks - # @!macro promises.warn.nil - # @!macro promises.param.timeout - # @!macro promises.param.timeout_value - # @param [Object] timeout_value a value returned by the method when it times out - # @return [Object, nil, timeout_value] the value of the Future when fulfilled, - # timeout_value on timeout, - # nil on rejection. - def value(timeout = nil, timeout_value = nil) - if wait_until_resolved timeout - internal_state.value - else - timeout_value - end - end - - # Returns reason of future's rejection. - # @!macro promises.touches - # - # @!macro promises.warn.blocks - # @!macro promises.warn.nil - # @!macro promises.param.timeout - # @!macro promises.param.timeout_value - # @return [Object, timeout_value] the reason, or timeout_value on timeout, or nil on fulfillment. - def reason(timeout = nil, timeout_value = nil) - if wait_until_resolved timeout - internal_state.reason - else - timeout_value - end - end - - # Returns triplet fulfilled?, value, reason. - # @!macro promises.touches - # - # @!macro promises.warn.blocks - # @!macro promises.param.timeout - # @return [Array(Boolean, Object, Object), nil] triplet of fulfilled?, value, reason, or nil - # on timeout. - def result(timeout = nil) - internal_state.result if wait_until_resolved timeout - end - - # @!macro promises.method.wait - # @raise [Exception] {#reason} on rejection - def wait!(timeout = nil) - result = wait_until_resolved!(timeout) - timeout ? result : self - end - - # @!macro promises.method.value - # @return [Object, nil, timeout_value] the value of the Future when fulfilled, - # or nil on rejection, - # or timeout_value on timeout. - # @raise [Exception] {#reason} on rejection - def value!(timeout = nil, timeout_value = nil) - if wait_until_resolved! timeout - internal_state.value - else - timeout_value - end - end - - # Allows rejected Future to be risen with `raise` method. - # If the reason is not an exception `Runtime.new(reason)` is returned. - # - # @example - # raise Promises.rejected_future(StandardError.new("boom")) - # raise Promises.rejected_future("or just boom") - # @raise [Concurrent::Error] when raising not rejected future - # @return [Exception] - def exception(*args) - raise Concurrent::Error, 'it is not rejected' unless rejected? - raise ArgumentError unless args.size <= 1 - reason = Array(internal_state.reason).flatten.compact - if reason.size > 1 - ex = Concurrent::MultipleErrors.new reason - ex.set_backtrace(caller) - ex - else - ex = if reason[0].respond_to? :exception - reason[0].exception(*args) - else - RuntimeError.new(reason[0]).exception(*args) - end - ex.set_backtrace Array(ex.backtrace) + caller - ex - end - end - - # @!macro promises.shortcut.on - # @return [Future] - def then(*args, &task) - then_on @DefaultExecutor, *args, &task - end - - # Chains the task to be executed asynchronously on executor after it fulfills. Does not run - # the task if it rejects. It will resolve though, triggering any dependent futures. - # - # @!macro promises.param.executor - # @!macro promises.param.args - # @!macro promise.param.task-future - # @return [Future] - # @yield [value, *args] to the task. - def then_on(executor, *args, &task) - ThenPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future - end - - # @!macro promises.shortcut.on - # @return [Future] - def rescue(*args, &task) - rescue_on @DefaultExecutor, *args, &task - end - - # Chains the task to be executed asynchronously on executor after it rejects. Does not run - # the task if it fulfills. It will resolve though, triggering any dependent futures. - # - # @!macro promises.param.executor - # @!macro promises.param.args - # @!macro promise.param.task-future - # @return [Future] - # @yield [reason, *args] to the task. - def rescue_on(executor, *args, &task) - RescuePromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future - end - - # @!macro promises.method.zip - # @return [Future] - def zip(other) - if other.is_a?(Future) - ZipFuturesPromise.new_blocked_by2(self, other, @DefaultExecutor).future - else - ZipFutureEventPromise.new_blocked_by2(self, other, @DefaultExecutor).future - end - end - - alias_method :&, :zip - - # Creates a new event which will be resolved when the first of receiver, `event_or_future` - # resolves. Returning future will have value nil if event_or_future is event and resolves - # first. - # - # @return [Future] - def any(event_or_future) - AnyResolvedFuturePromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).future - end - - alias_method :|, :any - - # Creates new future dependent on receiver which will not evaluate until touched, see {#touch}. - # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated. - # - # @return [Future] - def delay - event = DelayPromise.new(@DefaultExecutor).event - ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future - end - - # @!macro promise.method.schedule - # @return [Future] - def schedule(intended_time) - chain do - event = ScheduledPromise.new(@DefaultExecutor, intended_time).event - ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future - end.flat - end - - # @!macro promises.method.with_default_executor - # @return [Future] - def with_default_executor(executor) - FutureWrapperPromise.new_blocked_by1(self, executor).future - end - - # Creates new future which will have result of the future returned by receiver. If receiver - # rejects it will have its rejection. - # - # @param [Integer] level how many levels of futures should flatten - # @return [Future] - def flat_future(level = 1) - FlatFuturePromise.new_blocked_by1(self, level, @DefaultExecutor).future - end - - alias_method :flat, :flat_future - - # Creates new event which will be resolved when the returned event by receiver is. - # Be careful if the receiver rejects it will just resolve since Event does not hold reason. - # - # @return [Event] - def flat_event - FlatEventPromise.new_blocked_by1(self, @DefaultExecutor).event - end - - # @!macro promises.shortcut.using - # @return [self] - def on_fulfillment(*args, &callback) - on_fulfillment_using @DefaultExecutor, *args, &callback - end - - # Stores the callback to be executed synchronously on resolving thread after it is - # fulfilled. Does nothing on rejection. - # - # @!macro promises.param.args - # @!macro promise.param.callback - # @return [self] - # @yield [value, *args] to the callback. - def on_fulfillment!(*args, &callback) - add_callback :callback_on_fulfillment, args, callback - end - - # Stores the callback to be executed asynchronously on executor after it is - # fulfilled. Does nothing on rejection. - # - # @!macro promises.param.executor - # @!macro promises.param.args - # @!macro promise.param.callback - # @return [self] - # @yield [value, *args] to the callback. - def on_fulfillment_using(executor, *args, &callback) - add_callback :async_callback_on_fulfillment, executor, args, callback - end - - # @!macro promises.shortcut.using - # @return [self] - def on_rejection(*args, &callback) - on_rejection_using @DefaultExecutor, *args, &callback - end - - # Stores the callback to be executed synchronously on resolving thread after it is - # rejected. Does nothing on fulfillment. - # - # @!macro promises.param.args - # @!macro promise.param.callback - # @return [self] - # @yield [reason, *args] to the callback. - def on_rejection!(*args, &callback) - add_callback :callback_on_rejection, args, callback - end - - # Stores the callback to be executed asynchronously on executor after it is - # rejected. Does nothing on fulfillment. - # - # @!macro promises.param.executor - # @!macro promises.param.args - # @!macro promise.param.callback - # @return [self] - # @yield [reason, *args] to the callback. - def on_rejection_using(executor, *args, &callback) - add_callback :async_callback_on_rejection, executor, args, callback - end - - # Allows to use futures as green threads. The receiver has to evaluate to a future which - # represents what should be done next. It basically flattens indefinitely until non Future - # values is returned which becomes result of the returned future. Any encountered exception - # will become reason of the returned future. - # - # @return [Future] - # @param [#call(value)] run_test - # an object which when called returns either Future to keep running with - # or nil, then the run completes with the value. - # The run_test can be used to extract the Future from deeper structure, - # or to distinguish Future which is a resulting value from a future - # which is suppose to continue running. - # @example - # body = lambda do |v| - # v += 1 - # v < 5 ? Promises.future(v, &body) : v - # end - # Promises.future(0, &body).run.value! # => 5 - def run(run_test = method(:run_test)) - RunFuturePromise.new_blocked_by1(self, @DefaultExecutor, run_test).future - end - - # @!visibility private - def apply(args, block) - internal_state.apply args, block - end - - # Converts future to event which is resolved when future is resolved by fulfillment or rejection. - # - # @return [Event] - def to_event - event = Promises.resolvable_event - ensure - chain_resolvable(event) - end - - # Returns self, since this is a future - # @return [Future] - def to_future - self - end - - # @return [String] Short string representation. - def to_s - if resolved? - format '%s with %s>', super[0..-2], (fulfilled? ? value : reason).inspect - else - super - end - end - - alias_method :inspect, :to_s - - private - - def run_test(v) - v if v.is_a?(Future) - end - - def rejected_resolution(raise_on_reassign, state) - if raise_on_reassign - if internal_state == RESERVED - raise Concurrent::MultipleAssignmentError.new( - "Future can be resolved only once. It is already reserved.") - else - raise Concurrent::MultipleAssignmentError.new( - "Future can be resolved only once. It's #{result}, trying to set #{state.result}.", - current_result: result, - new_result: state.result) - end - end - return false - end - - def wait_until_resolved!(timeout = nil) - result = wait_until_resolved(timeout) - raise self if rejected? - result - end - - def async_callback_on_fulfillment(state, executor, args, callback) - with_async(executor, state, args, callback) do |st, ar, cb| - callback_on_fulfillment st, ar, cb - end - end - - def async_callback_on_rejection(state, executor, args, callback) - with_async(executor, state, args, callback) do |st, ar, cb| - callback_on_rejection st, ar, cb - end - end - - def callback_on_fulfillment(state, args, callback) - state.apply args, callback if state.fulfilled? - end - - def callback_on_rejection(state, args, callback) - state.apply args, callback unless state.fulfilled? - end - - def callback_on_resolution(state, args, callback) - callback.call(*state.result, *args) - end - - end - - # Marker module of Future, Event resolved manually. - module Resolvable - include InternalStates - end - - # A Event which can be resolved by user. - class ResolvableEvent < Event - include Resolvable - - # @!macro raise_on_reassign - # @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true. - - # @!macro promise.param.raise_on_reassign - # @param [Boolean] raise_on_reassign should method raise exception if already resolved - # @return [self, false] false is returner when raise_on_reassign is false and the receiver - # is already resolved. - # - - # Makes the event resolved, which triggers all dependent futures. - # - # @!macro promise.param.raise_on_reassign - # @!macro promise.param.reserved - # @param [true, false] reserved - # Set to true if the resolvable is {#reserve}d by you, - # marks resolution of reserved resolvable events and futures explicitly. - # Advanced feature, ignore unless you use {Resolvable#reserve} from edge. - def resolve(raise_on_reassign = true, reserved = false) - resolve_with RESOLVED, raise_on_reassign, reserved - end - - # Creates new event wrapping receiver, effectively hiding the resolve method. - # - # @return [Event] - def with_hidden_resolvable - @with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event - end - - # Behaves as {AbstractEventFuture#wait} but has one additional optional argument - # resolve_on_timeout. - # - # @param [true, false] resolve_on_timeout - # If it times out and the argument is true it will also resolve the event. - # @return [self, true, false] - # @see AbstractEventFuture#wait - def wait(timeout = nil, resolve_on_timeout = false) - super(timeout) or if resolve_on_timeout - # if it fails to resolve it was resolved in the meantime - # so return true as if there was no timeout - !resolve(false) - else - false - end - end - end - - # A Future which can be resolved by user. - class ResolvableFuture < Future - include Resolvable - - # Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`, - # which triggers all dependent futures. - # - # @param [true, false] fulfilled - # @param [Object] value - # @param [Object] reason - # @!macro promise.param.raise_on_reassign - # @!macro promise.param.reserved - def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true, reserved = false) - resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign, reserved) - end - - # Makes the future fulfilled with `value`, - # which triggers all dependent futures. - # - # @param [Object] value - # @!macro promise.param.raise_on_reassign - # @!macro promise.param.reserved - def fulfill(value, raise_on_reassign = true, reserved = false) - resolve_with Fulfilled.new(value), raise_on_reassign, reserved - end - - # Makes the future rejected with `reason`, - # which triggers all dependent futures. - # - # @param [Object] reason - # @!macro promise.param.raise_on_reassign - # @!macro promise.param.reserved - def reject(reason, raise_on_reassign = true, reserved = false) - resolve_with Rejected.new(reason), raise_on_reassign, reserved - end - - # Evaluates the block and sets its result as future's value fulfilling, if the block raises - # an exception the future rejects with it. - # - # @yield [*args] to the block. - # @yieldreturn [Object] value - # @return [self] - def evaluate_to(*args, &block) - promise.evaluate_to(*args, block) - end - - # Evaluates the block and sets its result as future's value fulfilling, if the block raises - # an exception the future rejects with it. - # - # @yield [*args] to the block. - # @yieldreturn [Object] value - # @return [self] - # @raise [Exception] also raise reason on rejection. - def evaluate_to!(*args, &block) - promise.evaluate_to(*args, block).wait! - end - - # @!macro promises.resolvable.resolve_on_timeout - # @param [::Array(true, Object, nil), ::Array(false, nil, Exception), nil] resolve_on_timeout - # If it times out and the argument is not nil it will also resolve the future - # to the provided resolution. - - # Behaves as {AbstractEventFuture#wait} but has one additional optional argument - # resolve_on_timeout. - # - # @!macro promises.resolvable.resolve_on_timeout - # @return [self, true, false] - # @see AbstractEventFuture#wait - def wait(timeout = nil, resolve_on_timeout = nil) - super(timeout) or if resolve_on_timeout - # if it fails to resolve it was resolved in the meantime - # so return true as if there was no timeout - !resolve(*resolve_on_timeout, false) - else - false - end - end - - # Behaves as {Future#wait!} but has one additional optional argument - # resolve_on_timeout. - # - # @!macro promises.resolvable.resolve_on_timeout - # @return [self, true, false] - # @raise [Exception] {#reason} on rejection - # @see Future#wait! - def wait!(timeout = nil, resolve_on_timeout = nil) - super(timeout) or if resolve_on_timeout - if resolve(*resolve_on_timeout, false) - false - else - # if it fails to resolve it was resolved in the meantime - # so return true as if there was no timeout - raise self if rejected? - true - end - else - false - end - end - - # Behaves as {Future#value} but has one additional optional argument - # resolve_on_timeout. - # - # @!macro promises.resolvable.resolve_on_timeout - # @return [Object, timeout_value, nil] - # @see Future#value - def value(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) - if wait_until_resolved timeout - internal_state.value - else - if resolve_on_timeout - unless resolve(*resolve_on_timeout, false) - # if it fails to resolve it was resolved in the meantime - # so return value as if there was no timeout - return internal_state.value - end - end - timeout_value - end - end - - # Behaves as {Future#value!} but has one additional optional argument - # resolve_on_timeout. - # - # @!macro promises.resolvable.resolve_on_timeout - # @return [Object, timeout_value, nil] - # @raise [Exception] {#reason} on rejection - # @see Future#value! - def value!(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) - if wait_until_resolved! timeout - internal_state.value - else - if resolve_on_timeout - unless resolve(*resolve_on_timeout, false) - # if it fails to resolve it was resolved in the meantime - # so return value as if there was no timeout - raise self if rejected? - return internal_state.value - end - end - timeout_value - end - end - - # Behaves as {Future#reason} but has one additional optional argument - # resolve_on_timeout. - # - # @!macro promises.resolvable.resolve_on_timeout - # @return [Exception, timeout_value, nil] - # @see Future#reason - def reason(timeout = nil, timeout_value = nil, resolve_on_timeout = nil) - if wait_until_resolved timeout - internal_state.reason - else - if resolve_on_timeout - unless resolve(*resolve_on_timeout, false) - # if it fails to resolve it was resolved in the meantime - # so return value as if there was no timeout - return internal_state.reason - end - end - timeout_value - end - end - - # Behaves as {Future#result} but has one additional optional argument - # resolve_on_timeout. - # - # @!macro promises.resolvable.resolve_on_timeout - # @return [::Array(Boolean, Object, Exception), nil] - # @see Future#result - def result(timeout = nil, resolve_on_timeout = nil) - if wait_until_resolved timeout - internal_state.result - else - if resolve_on_timeout - unless resolve(*resolve_on_timeout, false) - # if it fails to resolve it was resolved in the meantime - # so return value as if there was no timeout - internal_state.result - end - end - # otherwise returns nil - end - end - - # Creates new future wrapping receiver, effectively hiding the resolve method and similar. - # - # @return [Future] - def with_hidden_resolvable - @with_hidden_resolvable ||= FutureWrapperPromise.new_blocked_by1(self, @DefaultExecutor).future - end - end - - # @abstract - # @private - class AbstractPromise < Synchronization::Object - safe_initialization! - include InternalStates - - def initialize(future) - super() - @Future = future - end - - def future - @Future - end - - alias_method :event, :future - - def default_executor - future.default_executor - end - - def state - future.state - end - - def touch - end - - def to_s - format '%s %s>', super[0..-2], @Future - end - - alias_method :inspect, :to_s - - def delayed_because - nil - end - - private - - def resolve_with(new_state, raise_on_reassign = true) - @Future.resolve_with(new_state, raise_on_reassign) - end - - # @return [Future] - def evaluate_to(*args, block) - resolve_with Fulfilled.new(block.call(*args)) - rescue Exception => error - resolve_with Rejected.new(error) - raise error unless error.is_a?(StandardError) - end - end - - class ResolvableEventPromise < AbstractPromise - def initialize(default_executor) - super ResolvableEvent.new(self, default_executor) - end - end - - class ResolvableFuturePromise < AbstractPromise - def initialize(default_executor) - super ResolvableFuture.new(self, default_executor) - end - - public :evaluate_to - end - - # @abstract - class InnerPromise < AbstractPromise - end - - # @abstract - class BlockedPromise < InnerPromise - - private_class_method :new - - def self.new_blocked_by1(blocker, *args, &block) - blocker_delayed = blocker.promise.delayed_because - promise = new(blocker_delayed, 1, *args, &block) - blocker.add_callback_notify_blocked promise, 0 - promise - end - - def self.new_blocked_by2(blocker1, blocker2, *args, &block) - blocker_delayed1 = blocker1.promise.delayed_because - blocker_delayed2 = blocker2.promise.delayed_because - delayed = if blocker_delayed1 && blocker_delayed2 - # TODO (pitr-ch 23-Dec-2016): use arrays when we know it will not grow (only flat adds delay) - LockFreeStack.of2(blocker_delayed1, blocker_delayed2) - else - blocker_delayed1 || blocker_delayed2 - end - promise = new(delayed, 2, *args, &block) - blocker1.add_callback_notify_blocked promise, 0 - blocker2.add_callback_notify_blocked promise, 1 - promise - end - - def self.new_blocked_by(blockers, *args, &block) - delayed = blockers.reduce(nil) { |d, f| add_delayed d, f.promise.delayed_because } - promise = new(delayed, blockers.size, *args, &block) - blockers.each_with_index { |f, i| f.add_callback_notify_blocked promise, i } - promise - end - - def self.add_delayed(delayed1, delayed2) - if delayed1 && delayed2 - delayed1.push delayed2 - delayed1 - else - delayed1 || delayed2 - end - end - - def initialize(delayed, blockers_count, future) - super(future) - @Delayed = delayed - @Countdown = AtomicFixnum.new blockers_count - end - - def on_blocker_resolution(future, index) - countdown = process_on_blocker_resolution(future, index) - resolvable = resolvable?(countdown, future, index) - - on_resolvable(future, index) if resolvable - end - - def delayed_because - @Delayed - end - - def touch - clear_and_propagate_touch - end - - # for inspection only - def blocked_by - blocked_by = [] - ObjectSpace.each_object(AbstractEventFuture) { |o| blocked_by.push o if o.blocks.include? self } - blocked_by - end - - private - - def clear_and_propagate_touch(stack_or_element = @Delayed) - return if stack_or_element.nil? - - if stack_or_element.is_a? LockFreeStack - stack_or_element.clear_each { |element| clear_and_propagate_touch element } - else - stack_or_element.touch unless stack_or_element.nil? # if still present - end - end - - # @return [true,false] if resolvable - def resolvable?(countdown, future, index) - countdown.zero? - end - - def process_on_blocker_resolution(future, index) - @Countdown.decrement - end - - def on_resolvable(resolved_future, index) - raise NotImplementedError - end - end - - # @abstract - class BlockedTaskPromise < BlockedPromise - def initialize(delayed, blockers_count, default_executor, executor, args, &task) - raise ArgumentError, 'no block given' unless block_given? - super delayed, 1, Future.new(self, default_executor) - @Executor = executor - @Task = task - @Args = args - end - - def executor - @Executor - end - end - - class ThenPromise < BlockedTaskPromise - private - - def initialize(delayed, blockers_count, default_executor, executor, args, &task) - super delayed, blockers_count, default_executor, executor, args, &task - end - - def on_resolvable(resolved_future, index) - if resolved_future.fulfilled? - Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| - evaluate_to lambda { future.apply args, task } - end - else - resolve_with resolved_future.internal_state - end - end - end - - class RescuePromise < BlockedTaskPromise - private - - def initialize(delayed, blockers_count, default_executor, executor, args, &task) - super delayed, blockers_count, default_executor, executor, args, &task - end - - def on_resolvable(resolved_future, index) - if resolved_future.rejected? - Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| - evaluate_to lambda { future.apply args, task } - end - else - resolve_with resolved_future.internal_state - end - end - end - - class ChainPromise < BlockedTaskPromise - private - - def on_resolvable(resolved_future, index) - if Future === resolved_future - Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task| - evaluate_to(*future.result, *args, task) - end - else - Concurrent.executor(@Executor).post(@Args, @Task) do |args, task| - evaluate_to(*args, task) - end - end - end - end - - # will be immediately resolved - class ImmediateEventPromise < InnerPromise - def initialize(default_executor) - super Event.new(self, default_executor).resolve_with(RESOLVED) - end - end - - class ImmediateFuturePromise < InnerPromise - def initialize(default_executor, fulfilled, value, reason) - super Future.new(self, default_executor). - resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason)) - end - end - - class AbstractFlatPromise < BlockedPromise - - def initialize(delayed_because, blockers_count, event_or_future) - delayed = LockFreeStack.of1(self) - super(delayed, blockers_count, event_or_future) - # noinspection RubyArgCount - @Touched = AtomicBoolean.new false - @DelayedBecause = delayed_because || LockFreeStack.new - - event_or_future.add_callback_clear_delayed_node delayed.peek - end - - def touch - if @Touched.make_true - clear_and_propagate_touch @DelayedBecause - end - end - - private - - def touched? - @Touched.value - end - - def on_resolvable(resolved_future, index) - resolve_with resolved_future.internal_state - end - - def resolvable?(countdown, future, index) - !@Future.internal_state.resolved? && super(countdown, future, index) - end - - def add_delayed_of(future) - delayed = future.promise.delayed_because - if touched? - clear_and_propagate_touch delayed - else - BlockedPromise.add_delayed @DelayedBecause, delayed - clear_and_propagate_touch @DelayedBecause if touched? - end - end - - end - - class FlatEventPromise < AbstractFlatPromise - - private - - def initialize(delayed, blockers_count, default_executor) - super delayed, 2, Event.new(self, default_executor) - end - - def process_on_blocker_resolution(future, index) - countdown = super(future, index) - if countdown.nonzero? - internal_state = future.internal_state - - unless internal_state.fulfilled? - resolve_with RESOLVED - return countdown - end - - value = internal_state.value - case value - when AbstractEventFuture - add_delayed_of value - value.add_callback_notify_blocked self, nil - countdown - else - resolve_with RESOLVED - end - end - countdown - end - - end - - class FlatFuturePromise < AbstractFlatPromise - - private - - def initialize(delayed, blockers_count, levels, default_executor) - raise ArgumentError, 'levels has to be higher than 0' if levels < 1 - # flat promise may result to a future having delayed futures, therefore we have to have empty stack - # to be able to add new delayed futures - super delayed || LockFreeStack.new, 1 + levels, Future.new(self, default_executor) - end - - def process_on_blocker_resolution(future, index) - countdown = super(future, index) - if countdown.nonzero? - internal_state = future.internal_state - - unless internal_state.fulfilled? - resolve_with internal_state - return countdown - end - - value = internal_state.value - case value - when AbstractEventFuture - add_delayed_of value - value.add_callback_notify_blocked self, nil - countdown - else - evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" }) - end - end - countdown - end - - end - - class RunFuturePromise < AbstractFlatPromise - - private - - def initialize(delayed, blockers_count, default_executor, run_test) - super delayed, 1, Future.new(self, default_executor) - @RunTest = run_test - end - - def process_on_blocker_resolution(future, index) - internal_state = future.internal_state - - unless internal_state.fulfilled? - resolve_with internal_state - return 0 - end - - value = internal_state.value - continuation_future = @RunTest.call value - - if continuation_future - add_delayed_of continuation_future - continuation_future.add_callback_notify_blocked self, nil - else - resolve_with internal_state - end - - 1 - end - end - - class ZipEventEventPromise < BlockedPromise - def initialize(delayed, blockers_count, default_executor) - super delayed, 2, Event.new(self, default_executor) - end - - private - - def on_resolvable(resolved_future, index) - resolve_with RESOLVED - end - end - - class ZipFutureEventPromise < BlockedPromise - def initialize(delayed, blockers_count, default_executor) - super delayed, 2, Future.new(self, default_executor) - @result = nil - end - - private - - def process_on_blocker_resolution(future, index) - # first blocking is future, take its result - @result = future.internal_state if index == 0 - # super has to be called after above to piggyback on volatile @Countdown - super future, index - end - - def on_resolvable(resolved_future, index) - resolve_with @result - end - end - - class EventWrapperPromise < BlockedPromise - def initialize(delayed, blockers_count, default_executor) - super delayed, 1, Event.new(self, default_executor) - end - - private - - def on_resolvable(resolved_future, index) - resolve_with RESOLVED - end - end - - class FutureWrapperPromise < BlockedPromise - def initialize(delayed, blockers_count, default_executor) - super delayed, 1, Future.new(self, default_executor) - end - - private - - def on_resolvable(resolved_future, index) - resolve_with resolved_future.internal_state - end - end - - class ZipFuturesPromise < BlockedPromise - - private - - def initialize(delayed, blockers_count, default_executor) - super(delayed, blockers_count, Future.new(self, default_executor)) - @Resolutions = ::Array.new(blockers_count, nil) - - on_resolvable nil, nil if blockers_count == 0 - end - - def process_on_blocker_resolution(future, index) - # TODO (pitr-ch 18-Dec-2016): Can we assume that array will never break under parallel access when never re-sized? - @Resolutions[index] = future.internal_state # has to be set before countdown in super - super future, index - end - - def on_resolvable(resolved_future, index) - all_fulfilled = true - values = ::Array.new(@Resolutions.size) - reasons = ::Array.new(@Resolutions.size) - - @Resolutions.each_with_index do |internal_state, i| - fulfilled, values[i], reasons[i] = internal_state.result - all_fulfilled &&= fulfilled - end - - if all_fulfilled - resolve_with FulfilledArray.new(values) - else - resolve_with PartiallyRejected.new(values, reasons) - end - end - end - - class ZipEventsPromise < BlockedPromise - - private - - def initialize(delayed, blockers_count, default_executor) - super delayed, blockers_count, Event.new(self, default_executor) - - on_resolvable nil, nil if blockers_count == 0 - end - - def on_resolvable(resolved_future, index) - resolve_with RESOLVED - end - end - - # @abstract - class AbstractAnyPromise < BlockedPromise - end - - class AnyResolvedEventPromise < AbstractAnyPromise - - private - - def initialize(delayed, blockers_count, default_executor) - super delayed, blockers_count, Event.new(self, default_executor) - end - - def resolvable?(countdown, future, index) - true - end - - def on_resolvable(resolved_future, index) - resolve_with RESOLVED, false - end - end - - class AnyResolvedFuturePromise < AbstractAnyPromise - - private - - def initialize(delayed, blockers_count, default_executor) - super delayed, blockers_count, Future.new(self, default_executor) - end - - def resolvable?(countdown, future, index) - true - end - - def on_resolvable(resolved_future, index) - resolve_with resolved_future.internal_state, false - end - end - - class AnyFulfilledFuturePromise < AnyResolvedFuturePromise - - private - - def resolvable?(countdown, future, index) - future.fulfilled? || - # inlined super from BlockedPromise - countdown.zero? - end - end - - class DelayPromise < InnerPromise - - def initialize(default_executor) - event = Event.new(self, default_executor) - @Delayed = LockFreeStack.of1(self) - super event - event.add_callback_clear_delayed_node @Delayed.peek - end - - def touch - @Future.resolve_with RESOLVED - end - - def delayed_because - @Delayed - end - - end - - class ScheduledPromise < InnerPromise - def intended_time - @IntendedTime - end - - def inspect - "#{to_s[0..-2]} intended_time: #{@IntendedTime}>" - end - - private - - def initialize(default_executor, intended_time) - super Event.new(self, default_executor) - - @IntendedTime = intended_time - - in_seconds = begin - now = Time.now - schedule_time = if @IntendedTime.is_a? Time - @IntendedTime - else - now + @IntendedTime - end - [0, schedule_time.to_f - now.to_f].max - end - - Concurrent.global_timer_set.post(in_seconds) do - @Future.resolve_with RESOLVED - end - end - end - - extend FactoryMethods - - private_constant :AbstractPromise, - :ResolvableEventPromise, - :ResolvableFuturePromise, - :InnerPromise, - :BlockedPromise, - :BlockedTaskPromise, - :ThenPromise, - :RescuePromise, - :ChainPromise, - :ImmediateEventPromise, - :ImmediateFuturePromise, - :AbstractFlatPromise, - :FlatFuturePromise, - :FlatEventPromise, - :RunFuturePromise, - :ZipEventEventPromise, - :ZipFutureEventPromise, - :EventWrapperPromise, - :FutureWrapperPromise, - :ZipFuturesPromise, - :ZipEventsPromise, - :AbstractAnyPromise, - :AnyResolvedFuturePromise, - :AnyFulfilledFuturePromise, - :AnyResolvedEventPromise, - :DelayPromise, - :ScheduledPromise - - - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/re_include.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -module Concurrent - - # Methods form module A included to a module B, which is already included into class C, - # will not be visible in the C class. If this module is extended to B then A's methods - # are correctly made visible to C. - # - # @example - # module A - # def a - # :a - # end - # end - # - # module B1 - # end - # - # class C1 - # include B1 - # end - # - # module B2 - # extend Concurrent::ReInclude - # end - # - # class C2 - # include B2 - # end - # - # B1.send :include, A - # B2.send :include, A - # - # C1.new.respond_to? :a # => false - # C2.new.respond_to? :a # => true - module ReInclude - # @!visibility private - def included(base) - (@re_include_to_bases ||= []) << [:include, base] - super(base) - end - - # @!visibility private - def extended(base) - (@re_include_to_bases ||= []) << [:extend, base] - super(base) - end - - # @!visibility private - def include(*modules) - result = super(*modules) - modules.reverse.each do |module_being_included| - (@re_include_to_bases ||= []).each do |method, mod| - mod.send method, module_being_included - end - end - result - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/scheduled_task.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,318 +0,0 @@ -require 'concurrent/constants' -require 'concurrent/errors' -require 'concurrent/configuration' -require 'concurrent/ivar' -require 'concurrent/collection/copy_on_notify_observer_set' -require 'concurrent/utility/monotonic_time' - -require 'concurrent/options' - -module Concurrent - - # `ScheduledTask` is a close relative of `Concurrent::Future` but with one - # important difference: A `Future` is set to execute as soon as possible - # whereas a `ScheduledTask` is set to execute after a specified delay. This - # implementation is loosely based on Java's - # [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html). - # It is a more feature-rich variant of {Concurrent.timer}. - # - # The *intended* schedule time of task execution is set on object construction - # with the `delay` argument. The delay is a numeric (floating point or integer) - # representing a number of seconds in the future. Any other value or a numeric - # equal to or less than zero will result in an exception. The *actual* schedule - # time of task execution is set when the `execute` method is called. - # - # The constructor can also be given zero or more processing options. Currently - # the only supported options are those recognized by the - # [Dereferenceable](Dereferenceable) module. - # - # The final constructor argument is a block representing the task to be performed. - # If no block is given an `ArgumentError` will be raised. - # - # **States** - # - # `ScheduledTask` mixes in the [Obligation](Obligation) module thus giving it - # "future" behavior. This includes the expected lifecycle states. `ScheduledTask` - # has one additional state, however. While the task (block) is being executed the - # state of the object will be `:processing`. This additional state is necessary - # because it has implications for task cancellation. - # - # **Cancellation** - # - # A `:pending` task can be cancelled using the `#cancel` method. A task in any - # other state, including `:processing`, cannot be cancelled. The `#cancel` - # method returns a boolean indicating the success of the cancellation attempt. - # A cancelled `ScheduledTask` cannot be restarted. It is immutable. - # - # **Obligation and Observation** - # - # The result of a `ScheduledTask` can be obtained either synchronously or - # asynchronously. `ScheduledTask` mixes in both the [Obligation](Obligation) - # module and the - # [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html) - # module from the Ruby standard library. With one exception `ScheduledTask` - # behaves identically to [Future](Observable) with regard to these modules. - # - # @!macro copy_options - # - # @example Basic usage - # - # require 'concurrent' - # require 'thread' # for Queue - # require 'open-uri' # for open(uri) - # - # class Ticker - # def get_year_end_closing(symbol, year) - # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m" - # data = open(uri) {|f| f.collect{|line| line.strip } } - # data[1].split(',')[4].to_f - # end - # end - # - # # Future - # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) } - # price.state #=> :pending - # sleep(1) # do other stuff - # price.value #=> 63.65 - # price.state #=> :fulfilled - # - # # ScheduledTask - # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) } - # task.state #=> :pending - # sleep(3) # do other stuff - # task.value #=> 25.96 - # - # @example Successful task execution - # - # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' } - # task.state #=> :unscheduled - # task.execute - # task.state #=> pending - # - # # wait for it... - # sleep(3) - # - # task.unscheduled? #=> false - # task.pending? #=> false - # task.fulfilled? #=> true - # task.rejected? #=> false - # task.value #=> 'What does the fox say?' - # - # @example One line creation and execution - # - # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute - # task.state #=> pending - # - # task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' } - # task.state #=> pending - # - # @example Failed task execution - # - # task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') } - # task.pending? #=> true - # - # # wait for it... - # sleep(3) - # - # task.unscheduled? #=> false - # task.pending? #=> false - # task.fulfilled? #=> false - # task.rejected? #=> true - # task.value #=> nil - # task.reason #=> # - # - # @example Task execution with observation - # - # observer = Class.new{ - # def update(time, value, reason) - # puts "The task completed at #{time} with value '#{value}'" - # end - # }.new - # - # task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' } - # task.add_observer(observer) - # task.execute - # task.pending? #=> true - # - # # wait for it... - # sleep(3) - # - # #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?' - # - # @!macro monotonic_clock_warning - # - # @see Concurrent.timer - class ScheduledTask < IVar - include Comparable - - # The executor on which to execute the task. - # @!visibility private - attr_reader :executor - - # Schedule a task for execution at a specified future time. - # - # @param [Float] delay the number of seconds to wait for before executing the task - # - # @yield the task to be performed - # - # @!macro executor_and_deref_options - # - # @option opts [object, Array] :args zero or more arguments to be passed the task - # block on execution - # - # @raise [ArgumentError] When no block is given - # @raise [ArgumentError] When given a time that is in the past - def initialize(delay, opts = {}, &task) - raise ArgumentError.new('no block given') unless block_given? - raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0 - - super(NULL, opts, &nil) - - synchronize do - ns_set_state(:unscheduled) - @parent = opts.fetch(:timer_set, Concurrent.global_timer_set) - @args = get_arguments_from(opts) - @delay = delay.to_f - @task = task - @time = nil - @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor - self.observers = Collection::CopyOnNotifyObserverSet.new - end - end - - # The `delay` value given at instanciation. - # - # @return [Float] the initial delay. - def initial_delay - synchronize { @delay } - end - - # The monotonic time at which the the task is scheduled to be executed. - # - # @return [Float] the schedule time or nil if `unscheduled` - def schedule_time - synchronize { @time } - end - - # Comparator which orders by schedule time. - # - # @!visibility private - def <=>(other) - schedule_time <=> other.schedule_time - end - - # Has the task been cancelled? - # - # @return [Boolean] true if the task is in the given state else false - def cancelled? - synchronize { ns_check_state?(:cancelled) } - end - - # In the task execution in progress? - # - # @return [Boolean] true if the task is in the given state else false - def processing? - synchronize { ns_check_state?(:processing) } - end - - # Cancel this task and prevent it from executing. A task can only be - # cancelled if it is pending or unscheduled. - # - # @return [Boolean] true if successfully cancelled else false - def cancel - if compare_and_set_state(:cancelled, :pending, :unscheduled) - complete(false, nil, CancelledOperationError.new) - # To avoid deadlocks this call must occur outside of #synchronize - # Changing the state above should prevent redundant calls - @parent.send(:remove_task, self) - else - false - end - end - - # Reschedule the task using the original delay and the current time. - # A task can only be reset while it is `:pending`. - # - # @return [Boolean] true if successfully rescheduled else false - def reset - synchronize{ ns_reschedule(@delay) } - end - - # Reschedule the task using the given delay and the current time. - # A task can only be reset while it is `:pending`. - # - # @param [Float] delay the number of seconds to wait for before executing the task - # - # @return [Boolean] true if successfully rescheduled else false - # - # @raise [ArgumentError] When given a time that is in the past - def reschedule(delay) - delay = delay.to_f - raise ArgumentError.new('seconds must be greater than zero') if delay < 0.0 - synchronize{ ns_reschedule(delay) } - end - - # Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending` - # and starts counting down toward execution. Does nothing if the `ScheduledTask` is - # in any state other than `:unscheduled`. - # - # @return [ScheduledTask] a reference to `self` - def execute - if compare_and_set_state(:pending, :unscheduled) - synchronize{ ns_schedule(@delay) } - end - self - end - - # Create a new `ScheduledTask` object with the given block, execute it, and return the - # `:pending` object. - # - # @param [Float] delay the number of seconds to wait for before executing the task - # - # @!macro executor_and_deref_options - # - # @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state - # - # @raise [ArgumentError] if no block is given - def self.execute(delay, opts = {}, &task) - new(delay, opts, &task).execute - end - - # Execute the task. - # - # @!visibility private - def process_task - safe_execute(@task, @args) - end - - protected :set, :try_set, :fail, :complete - - protected - - # Schedule the task using the given delay and the current time. - # - # @param [Float] delay the number of seconds to wait for before executing the task - # - # @return [Boolean] true if successfully rescheduled else false - # - # @!visibility private - def ns_schedule(delay) - @delay = delay - @time = Concurrent.monotonic_time + @delay - @parent.send(:post_task, self) - end - - # Reschedule the task using the given delay and the current time. - # A task can only be reset while it is `:pending`. - # - # @param [Float] delay the number of seconds to wait for before executing the task - # - # @return [Boolean] true if successfully rescheduled else false - # - # @!visibility private - def ns_reschedule(delay) - return false unless ns_check_state?(:pending) - @parent.send(:remove_task, self) && ns_schedule(delay) - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/set.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -require 'concurrent/utility/engine' -require 'concurrent/thread_safe/util' -require 'set' - -module Concurrent - - # @!macro concurrent_set - # - # A thread-safe subclass of Set. This version locks against the object - # itself for every method call, ensuring only one thread can be reading - # or writing at a time. This includes iteration methods like `#each`. - # - # @note `a += b` is **not** a **thread-safe** operation on - # `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set` - # which is union of `a` and `b`, then it writes the union to `a`. - # The read and write are independent operations they do not form a single atomic - # operation therefore when two `+=` operations are executed concurrently updates - # may be lost. Use `#merge` instead. - # - # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set` - - - # @!macro internal_implementation_note - SetImplementation = case - when Concurrent.on_cruby? - # Because MRI never runs code in parallel, the existing - # non-thread-safe structures should usually work fine. - ::Set - - when Concurrent.on_jruby? - require 'jruby/synchronized' - - class JRubySet < ::Set - include JRuby::Synchronized - end - JRubySet - - when Concurrent.on_rbx? - require 'monitor' - require 'concurrent/thread_safe/util/data_structures' - - class RbxSet < ::Set - end - ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet - RbxSet - - when Concurrent.on_truffleruby? - require 'concurrent/thread_safe/util/data_structures' - - class TruffleRubySet < ::Set - end - - ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet - TruffleRubySet - - else - warn 'Possibly unsupported Ruby implementation' - ::Set - end - private_constant :SetImplementation - - # @!macro concurrent_set - class Set < SetImplementation - end -end - diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/settable_struct.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -require 'concurrent/synchronization/abstract_struct' -require 'concurrent/errors' -require 'concurrent/synchronization' - -module Concurrent - - # An thread-safe, write-once variation of Ruby's standard `Struct`. - # Each member can have its value set at most once, either at construction - # or any time thereafter. Attempting to assign a value to a member - # that has already been set will result in a `Concurrent::ImmutabilityError`. - # - # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct` - # @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword - module SettableStruct - include Synchronization::AbstractStruct - - # @!macro struct_values - def values - synchronize { ns_values } - end - alias_method :to_a, :values - - # @!macro struct_values_at - def values_at(*indexes) - synchronize { ns_values_at(indexes) } - end - - # @!macro struct_inspect - def inspect - synchronize { ns_inspect } - end - alias_method :to_s, :inspect - - # @!macro struct_merge - def merge(other, &block) - synchronize { ns_merge(other, &block) } - end - - # @!macro struct_to_h - def to_h - synchronize { ns_to_h } - end - - # @!macro struct_get - def [](member) - synchronize { ns_get(member) } - end - - # @!macro struct_equality - def ==(other) - synchronize { ns_equality(other) } - end - - # @!macro struct_each - def each(&block) - return enum_for(:each) unless block_given? - synchronize { ns_each(&block) } - end - - # @!macro struct_each_pair - def each_pair(&block) - return enum_for(:each_pair) unless block_given? - synchronize { ns_each_pair(&block) } - end - - # @!macro struct_select - def select(&block) - return enum_for(:select) unless block_given? - synchronize { ns_select(&block) } - end - - # @!macro struct_set - # - # @raise [Concurrent::ImmutabilityError] if the given member has already been set - def []=(member, value) - if member.is_a? Integer - length = synchronize { @values.length } - if member >= length - raise IndexError.new("offset #{member} too large for struct(size:#{length})") - end - synchronize do - unless @values[member].nil? - raise Concurrent::ImmutabilityError.new('struct member has already been set') - end - @values[member] = value - end - else - send("#{member}=", value) - end - rescue NoMethodError - raise NameError.new("no member '#{member}' in struct") - end - - # @!macro struct_new - def self.new(*args, &block) - clazz_name = nil - if args.length == 0 - raise ArgumentError.new('wrong number of arguments (0 for 1+)') - elsif args.length > 0 && args.first.is_a?(String) - clazz_name = args.shift - end - FACTORY.define_struct(clazz_name, args, &block) - end - - FACTORY = Class.new(Synchronization::LockableObject) do - def define_struct(name, members, &block) - synchronize do - clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block) - members.each_with_index do |member, index| - clazz.send :remove_method, member if clazz.instance_methods.include? member - clazz.send(:define_method, member) do - synchronize { @values[index] } - end - clazz.send(:define_method, "#{member}=") do |value| - synchronize do - unless @values[index].nil? - raise Concurrent::ImmutabilityError.new('struct member has already been set') - end - @values[index] = value - end - end - end - clazz - end - end - end.new - private_constant :FACTORY - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_lockable_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,98 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - class AbstractLockableObject < Synchronization::Object - - protected - - # @!macro synchronization_object_method_synchronize - # - # @yield runs the block synchronized against this object, - # equivalent of java's `synchronize(this) {}` - # @note can by made public in descendants if required by `public :synchronize` - def synchronize - raise NotImplementedError - end - - # @!macro synchronization_object_method_ns_wait_until - # - # Wait until condition is met or timeout passes, - # protects against spurious wake-ups. - # @param [Numeric, nil] timeout in seconds, `nil` means no timeout - # @yield condition to be met - # @yieldreturn [true, false] - # @return [true, false] if condition met - # @note only to be used inside synchronized block - # @note to provide direct access to this method in a descendant add method - # ``` - # def wait_until(timeout = nil, &condition) - # synchronize { ns_wait_until(timeout, &condition) } - # end - # ``` - def ns_wait_until(timeout = nil, &condition) - if timeout - wait_until = Concurrent.monotonic_time + timeout - loop do - now = Concurrent.monotonic_time - condition_result = condition.call - return condition_result if now >= wait_until || condition_result - ns_wait wait_until - now - end - else - ns_wait timeout until condition.call - true - end - end - - # @!macro synchronization_object_method_ns_wait - # - # Wait until another thread calls #signal or #broadcast, - # spurious wake-ups can happen. - # - # @param [Numeric, nil] timeout in seconds, `nil` means no timeout - # @return [self] - # @note only to be used inside synchronized block - # @note to provide direct access to this method in a descendant add method - # ``` - # def wait(timeout = nil) - # synchronize { ns_wait(timeout) } - # end - # ``` - def ns_wait(timeout = nil) - raise NotImplementedError - end - - # @!macro synchronization_object_method_ns_signal - # - # Signal one waiting thread. - # @return [self] - # @note only to be used inside synchronized block - # @note to provide direct access to this method in a descendant add method - # ``` - # def signal - # synchronize { ns_signal } - # end - # ``` - def ns_signal - raise NotImplementedError - end - - # @!macro synchronization_object_method_ns_broadcast - # - # Broadcast to all waiting threads. - # @return [self] - # @note only to be used inside synchronized block - # @note to provide direct access to this method in a descendant add method - # ``` - # def broadcast - # synchronize { ns_broadcast } - # end - # ``` - def ns_broadcast - raise NotImplementedError - end - - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # @!macro internal_implementation_note - class AbstractObject - - # @abstract has to be implemented based on Ruby runtime - def initialize - raise NotImplementedError - end - - # @!visibility private - # @abstract - def full_memory_barrier - raise NotImplementedError - end - - def self.attr_volatile(*names) - raise NotImplementedError - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/abstract_struct.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,160 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # @!macro internal_implementation_note - module AbstractStruct - - # @!visibility private - def initialize(*values) - super() - ns_initialize(*values) - end - - # @!macro struct_length - # - # Returns the number of struct members. - # - # @return [Fixnum] the number of struct members - def length - self.class::MEMBERS.length - end - alias_method :size, :length - - # @!macro struct_members - # - # Returns the struct members as an array of symbols. - # - # @return [Array] the struct members as an array of symbols - def members - self.class::MEMBERS.dup - end - - protected - - # @!macro struct_values - # - # @!visibility private - def ns_values - @values.dup - end - - # @!macro struct_values_at - # - # @!visibility private - def ns_values_at(indexes) - @values.values_at(*indexes) - end - - # @!macro struct_to_h - # - # @!visibility private - def ns_to_h - length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo} - end - - # @!macro struct_get - # - # @!visibility private - def ns_get(member) - if member.is_a? Integer - if member >= @values.length - raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})") - end - @values[member] - else - send(member) - end - rescue NoMethodError - raise NameError.new("no member '#{member}' in struct") - end - - # @!macro struct_equality - # - # @!visibility private - def ns_equality(other) - self.class == other.class && self.values == other.values - end - - # @!macro struct_each - # - # @!visibility private - def ns_each - values.each{|value| yield value } - end - - # @!macro struct_each_pair - # - # @!visibility private - def ns_each_pair - @values.length.times do |index| - yield self.class::MEMBERS[index], @values[index] - end - end - - # @!macro struct_select - # - # @!visibility private - def ns_select - values.select{|value| yield value } - end - - # @!macro struct_inspect - # - # @!visibility private - def ns_inspect - struct = pr_underscore(self.class.ancestors[1]) - clazz = ((self.class.to_s =~ /^#" - end - - # @!macro struct_merge - # - # @!visibility private - def ns_merge(other, &block) - self.class.new(*self.to_h.merge(other, &block).values) - end - - # @!visibility private - def pr_underscore(clazz) - word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229 - word.gsub!(/::/, '/') - word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') - word.gsub!(/([a-z\d])([A-Z])/,'\1_\2') - word.tr!("-", "_") - word.downcase! - word - end - - # @!visibility private - def self.define_struct_class(parent, base, name, members, &block) - clazz = Class.new(base || Object) do - include parent - self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze) - def ns_initialize(*values) - raise ArgumentError.new('struct size differs') if values.length > length - @values = values.fill(nil, values.length..length-1) - end - end - unless name.nil? - begin - parent.send :remove_const, name if parent.const_defined?(name, false) - parent.const_set(name, clazz) - clazz - rescue NameError - raise NameError.new("identifier #{name} needs to be constant") - end - end - members.each_with_index do |member, index| - clazz.send :remove_method, member if clazz.instance_methods.include? member - clazz.send(:define_method, member) do - @values[index] - end - end - clazz.class_exec(&block) unless block.nil? - clazz.singleton_class.send :alias_method, :[], :new - clazz - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/condition.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # TODO (pitr-ch 04-Dec-2016): should be in edge - class Condition < LockableObject - safe_initialization! - - # TODO (pitr 12-Sep-2015): locks two objects, improve - # TODO (pitr 26-Sep-2015): study - # http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/concurrent/locks/AbstractQueuedSynchronizer.java#AbstractQueuedSynchronizer.Node - - singleton_class.send :alias_method, :private_new, :new - private_class_method :new - - def initialize(lock) - super() - @Lock = lock - end - - def wait(timeout = nil) - @Lock.synchronize { ns_wait(timeout) } - end - - def ns_wait(timeout = nil) - synchronize { super(timeout) } - end - - def wait_until(timeout = nil, &condition) - @Lock.synchronize { ns_wait_until(timeout, &condition) } - end - - def ns_wait_until(timeout = nil, &condition) - synchronize { super(timeout, &condition) } - end - - def signal - @Lock.synchronize { ns_signal } - end - - def ns_signal - synchronize { super } - end - - def broadcast - @Lock.synchronize { ns_broadcast } - end - - def ns_broadcast - synchronize { super } - end - end - - class LockableObject < LockableObjectImplementation - def new_condition - Condition.private_new(self) - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_lockable_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -module Concurrent - module Synchronization - - if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? - - # @!visibility private - # @!macro internal_implementation_note - class JRubyLockableObject < AbstractLockableObject - - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/jruby_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -module Concurrent - module Synchronization - - if Concurrent.on_jruby? && Concurrent.java_extensions_loaded? - - # @!visibility private - module JRubyAttrVolatile - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def attr_volatile(*names) - names.each do |name| - - ivar = :"@volatile_#{name}" - - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{name} - instance_variable_get_volatile(:#{ivar}) - end - - def #{name}=(value) - instance_variable_set_volatile(:#{ivar}, value) - end - RUBY - - end - names.map { |n| [n, :"#{n}="] }.flatten - end - end - end - - # @!visibility private - # @!macro internal_implementation_note - class JRubyObject < AbstractObject - include JRubyAttrVolatile - - def initialize - # nothing to do - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lock.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # TODO (pitr-ch 04-Dec-2016): should be in edge - class Lock < LockableObject - # TODO use JavaReentrantLock on JRuby - - public :synchronize - - def wait(timeout = nil) - synchronize { ns_wait(timeout) } - end - - public :ns_wait - - def wait_until(timeout = nil, &condition) - synchronize { ns_wait_until(timeout, &condition) } - end - - public :ns_wait_until - - def signal - synchronize { ns_signal } - end - - public :ns_signal - - def broadcast - synchronize { ns_broadcast } - end - - public :ns_broadcast - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/lockable_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # @!macro internal_implementation_note - LockableObjectImplementation = case - when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3) - MonitorLockableObject - when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3) - MutexLockableObject - when Concurrent.on_jruby? - JRubyLockableObject - when Concurrent.on_rbx? - RbxLockableObject - when Concurrent.on_truffleruby? - MutexLockableObject - else - warn 'Possibly unsupported Ruby implementation' - MonitorLockableObject - end - private_constant :LockableObjectImplementation - - # Safe synchronization under any Ruby implementation. - # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}. - # Provides a single layer which can improve its implementation over time without changes needed to - # the classes using it. Use {Synchronization::Object} not this abstract class. - # - # @note this object does not support usage together with - # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup) - # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise). - # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and - # `Thread#wakeup` will not work on all platforms. - # - # @see Event implementation as an example of this class use - # - # @example simple - # class AnClass < Synchronization::Object - # def initialize - # super - # synchronize { @value = 'asd' } - # end - # - # def value - # synchronize { @value } - # end - # end - # - # @!visibility private - class LockableObject < LockableObjectImplementation - - # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing - # TODO (pitr 12-Sep-2015): we inherit too much ourselves :/ - - # @!method initialize(*args, &block) - # @!macro synchronization_object_method_initialize - - # @!method synchronize - # @!macro synchronization_object_method_synchronize - - # @!method wait_until(timeout = nil, &condition) - # @!macro synchronization_object_method_ns_wait_until - - # @!method wait(timeout = nil) - # @!macro synchronization_object_method_ns_wait - - # @!method signal - # @!macro synchronization_object_method_ns_signal - - # @!method broadcast - # @!macro synchronization_object_method_ns_broadcast - - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mri_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - module MriAttrVolatile - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def attr_volatile(*names) - names.each do |name| - ivar = :"@volatile_#{name}" - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{name} - #{ivar} - end - - def #{name}=(value) - #{ivar} = value - end - RUBY - end - names.map { |n| [n, :"#{n}="] }.flatten - end - end - - def full_memory_barrier - # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars - # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211 - end - end - - # @!visibility private - # @!macro internal_implementation_note - class MriObject < AbstractObject - include MriAttrVolatile - - def initialize - # nothing to do - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/mutex_lockable_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -module Concurrent - # noinspection RubyInstanceVariableNamingConvention - module Synchronization - - # @!visibility private - # @!macro internal_implementation_note - module ConditionSignalling - protected - - def ns_signal - @__Condition__.signal - self - end - - def ns_broadcast - @__Condition__.broadcast - self - end - end - - - # @!visibility private - # @!macro internal_implementation_note - class MutexLockableObject < AbstractLockableObject - include ConditionSignalling - - safe_initialization! - - def initialize(*defaults) - super(*defaults) - @__Lock__ = ::Mutex.new - @__Condition__ = ::ConditionVariable.new - end - - protected - - def synchronize - if @__Lock__.owned? - yield - else - @__Lock__.synchronize { yield } - end - end - - def ns_wait(timeout = nil) - @__Condition__.wait @__Lock__, timeout - self - end - end - - # @!visibility private - # @!macro internal_implementation_note - class MonitorLockableObject < AbstractLockableObject - include ConditionSignalling - - safe_initialization! - - def initialize(*defaults) - super(*defaults) - @__Lock__ = ::Monitor.new - @__Condition__ = @__Lock__.new_cond - end - - protected - - def synchronize # TODO may be a problem with lock.synchronize { lock.wait } - @__Lock__.synchronize { yield } - end - - def ns_wait(timeout = nil) - @__Condition__.wait timeout - self - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,183 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # @!macro internal_implementation_note - ObjectImplementation = case - when Concurrent.on_cruby? - MriObject - when Concurrent.on_jruby? - JRubyObject - when Concurrent.on_rbx? - RbxObject - when Concurrent.on_truffleruby? - TruffleRubyObject - else - warn 'Possibly unsupported Ruby implementation' - MriObject - end - private_constant :ObjectImplementation - - # Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions. - # - final instance variables see {Object.safe_initialization!} - # - volatile instance variables see {Object.attr_volatile} - # - volatile instance variables see {Object.attr_atomic} - class Object < ObjectImplementation - # TODO make it a module if possible - - # @!method self.attr_volatile(*names) - # Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with - # volatile (Java) semantic. The instance variable should be accessed only through generated methods. - # - # @param [::Array] names of the instance variables to be volatile - # @return [::Array] names of defined method names - - # Has to be called by children. - def initialize - super - __initialize_atomic_fields__ - end - - # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that - # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures - # same behaviour as Java's final fields. - # @example - # class AClass < Concurrent::Synchronization::Object - # safe_initialization! - # - # def initialize - # @AFinalValue = 'value' # published safely, does not have to be synchronized - # end - # end - # @return [true] - def self.safe_initialization! - # define only once, and not again in children - return if safe_initialization? - - # @!visibility private - def self.new(*args, &block) - object = super(*args, &block) - ensure - object.full_memory_barrier if object - end - - @safe_initialization = true - end - - # @return [true, false] if this class is safely initialized. - def self.safe_initialization? - @safe_initialization = false unless defined? @safe_initialization - @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?) - end - - # For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains - # any instance variables with CamelCase names and isn't {.safe_initialization?}. - # @raise when offend found - # @return [true] - def self.ensure_safe_initialization_when_final_fields_are_present - Object.class_eval do - def self.new(*args, &block) - object = super(*args, &block) - ensure - has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ } - if has_final_field && !safe_initialization? - raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!" - end - end - end - true - end - - # Creates methods for reading and writing to a instance variable with - # volatile (Java) semantic as {.attr_volatile} does. - # The instance variable should be accessed oly through generated methods. - # This method generates following methods: `value`, `value=(new_value) #=> new_value`, - # `swap_value(new_value) #=> old_value`, - # `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`. - # @param [::Array] names of the instance variables to be volatile with CAS. - # @return [::Array] names of defined method names. - # @!macro attr_atomic - # @!method $1 - # @return [Object] The $1. - # @!method $1=(new_$1) - # Set the $1. - # @return [Object] new_$1. - # @!method swap_$1(new_$1) - # Set the $1 to new_$1 and return the old $1. - # @return [Object] old $1 - # @!method compare_and_set_$1(expected_$1, new_$1) - # Sets the $1 to new_$1 if the current $1 is expected_$1 - # @return [true, false] - # @!method update_$1(&block) - # Updates the $1 using the block. - # @yield [Object] Calculate a new $1 using given (old) $1 - # @yieldparam [Object] old $1 - # @return [Object] new $1 - def self.attr_atomic(*names) - @__atomic_fields__ ||= [] - @__atomic_fields__ += names - safe_initialization! - define_initialize_atomic_fields - - names.each do |name| - ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}" - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{name} - #{ivar}.get - end - - def #{name}=(value) - #{ivar}.set value - end - - def swap_#{name}(value) - #{ivar}.swap value - end - - def compare_and_set_#{name}(expected, value) - #{ivar}.compare_and_set expected, value - end - - def update_#{name}(&block) - #{ivar}.update(&block) - end - RUBY - end - names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] } - end - - # @param [true, false] inherited should inherited volatile with CAS fields be returned? - # @return [::Array] Returns defined volatile with CAS fields on this class. - def self.atomic_attributes(inherited = true) - @__atomic_fields__ ||= [] - ((superclass.atomic_attributes if superclass.respond_to?(:atomic_attributes) && inherited) || []) + @__atomic_fields__ - end - - # @return [true, false] is the attribute with name atomic? - def self.atomic_attribute?(name) - atomic_attributes.include? name - end - - private - - def self.define_initialize_atomic_fields - assignments = @__atomic_fields__.map do |name| - "@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)" - end.join("\n") - - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def __initialize_atomic_fields__ - super - #{assignments} - end - RUBY - end - - private_class_method :define_initialize_atomic_fields - - def __initialize_atomic_fields__ - end - - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_lockable_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - # @!macro internal_implementation_note - class RbxLockableObject < AbstractLockableObject - safe_initialization! - - def initialize(*defaults) - super(*defaults) - @__Waiters__ = [] - @__owner__ = nil - end - - protected - - def synchronize(&block) - if @__owner__ == Thread.current - yield - else - result = nil - Rubinius.synchronize(self) do - begin - @__owner__ = Thread.current - result = yield - ensure - @__owner__ = nil - end - end - result - end - end - - def ns_wait(timeout = nil) - wchan = Rubinius::Channel.new - - begin - @__Waiters__.push wchan - Rubinius.unlock(self) - signaled = wchan.receive_timeout timeout - ensure - Rubinius.lock(self) - - if !signaled && !@__Waiters__.delete(wchan) - # we timed out, but got signaled afterwards, - # so pass that signal on to the next waiter - @__Waiters__.shift << true unless @__Waiters__.empty? - end - end - - self - end - - def ns_signal - @__Waiters__.shift << true unless @__Waiters__.empty? - self - end - - def ns_broadcast - @__Waiters__.shift << true until @__Waiters__.empty? - self - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/rbx_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - module RbxAttrVolatile - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - - def attr_volatile(*names) - names.each do |name| - ivar = :"@volatile_#{name}" - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{name} - Rubinius.memory_barrier - #{ivar} - end - - def #{name}=(value) - #{ivar} = value - Rubinius.memory_barrier - end - RUBY - end - names.map { |n| [n, :"#{n}="] }.flatten - end - - end - - def full_memory_barrier - # Rubinius instance variables are not volatile so we need to insert barrier - # TODO (pitr 26-Nov-2015): check comments like ^ - Rubinius.memory_barrier - end - end - - # @!visibility private - # @!macro internal_implementation_note - class RbxObject < AbstractObject - include RbxAttrVolatile - - def initialize - # nothing to do - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/truffleruby_object.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -module Concurrent - module Synchronization - - # @!visibility private - module TruffleRubyAttrVolatile - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def attr_volatile(*names) - names.each do |name| - ivar = :"@volatile_#{name}" - - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{name} - full_memory_barrier - #{ivar} - end - - def #{name}=(value) - #{ivar} = value - full_memory_barrier - end - RUBY - end - - names.map { |n| [n, :"#{n}="] }.flatten - end - end - - def full_memory_barrier - TruffleRuby.full_memory_barrier - end - end - - # @!visibility private - # @!macro internal_implementation_note - class TruffleRubyObject < AbstractObject - include TruffleRubyAttrVolatile - - def initialize - # nothing to do - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization/volatile.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -module Concurrent - module Synchronization - - # Volatile adds the attr_volatile class method when included. - # - # @example - # class Foo - # include Concurrent::Synchronization::Volatile - # - # attr_volatile :bar - # - # def initialize - # self.bar = 1 - # end - # end - # - # foo = Foo.new - # foo.bar - # => 1 - # foo.bar = 2 - # => 2 - - Volatile = case - when Concurrent.on_cruby? - MriAttrVolatile - when Concurrent.on_jruby? - JRubyAttrVolatile - when Concurrent.on_rbx? - RbxAttrVolatile - when Concurrent.on_truffleruby? - TruffleRubyAttrVolatile - else - MriAttrVolatile - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/synchronization.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -require 'concurrent/utility/engine' - -require 'concurrent/synchronization/abstract_object' -require 'concurrent/utility/native_extension_loader' # load native parts first -Concurrent.load_native_extensions - -require 'concurrent/synchronization/mri_object' -require 'concurrent/synchronization/jruby_object' -require 'concurrent/synchronization/rbx_object' -require 'concurrent/synchronization/truffleruby_object' -require 'concurrent/synchronization/object' -require 'concurrent/synchronization/volatile' - -require 'concurrent/synchronization/abstract_lockable_object' -require 'concurrent/synchronization/mutex_lockable_object' -require 'concurrent/synchronization/jruby_lockable_object' -require 'concurrent/synchronization/rbx_lockable_object' - -require 'concurrent/synchronization/lockable_object' - -require 'concurrent/synchronization/condition' -require 'concurrent/synchronization/lock' - -module Concurrent - # {include:file:docs-source/synchronization.md} - # {include:file:docs-source/synchronization-notes.md} - module Synchronization - end -end - diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/synchronized_delegator.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -require 'delegate' -require 'monitor' - -module Concurrent - unless defined?(SynchronizedDelegator) - - # This class provides a trivial way to synchronize all calls to a given object - # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls - # around the delegated `#send`. Example: - # - # array = [] # not thread-safe on many impls - # array = SynchronizedDelegator.new([]) # thread-safe - # - # A simple `Monitor` provides a very coarse-grained way to synchronize a given - # object, in that it will cause synchronization for methods that have no need - # for it, but this is a trivial way to get thread-safety where none may exist - # currently on some implementations. - # - # This class is currently being considered for inclusion into stdlib, via - # https://bugs.ruby-lang.org/issues/8556 - # - # @!visibility private - class SynchronizedDelegator < SimpleDelegator - def setup - @old_abort = Thread.abort_on_exception - Thread.abort_on_exception = true - end - - def teardown - Thread.abort_on_exception = @old_abort - end - - def initialize(obj) - __setobj__(obj) - @monitor = Monitor.new - end - - def method_missing(method, *args, &block) - monitor = @monitor - begin - monitor.enter - super - ensure - monitor.exit - end - end - - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/adder.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -require 'concurrent/thread_safe/util' -require 'concurrent/thread_safe/util/striped64' - -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8 - # available in public domain. - # - # Original source code available here: - # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8 - # - # One or more variables that together maintain an initially zero - # sum. When updates (method +add+) are contended across threads, - # the set of variables may grow dynamically to reduce contention. - # Method +sum+ returns the current total combined across the - # variables maintaining the sum. - # - # This class is usually preferable to single +Atomic+ reference when - # multiple threads update a common sum that is used for purposes such - # as collecting statistics, not for fine-grained synchronization - # control. Under low update contention, the two classes have similar - # characteristics. But under high contention, expected throughput of - # this class is significantly higher, at the expense of higher space - # consumption. - # - # @!visibility private - class Adder < Striped64 - # Adds the given value. - def add(x) - if (current_cells = cells) || !cas_base_computed {|current_base| current_base + x} - was_uncontended = true - hash = hash_code - unless current_cells && (cell = current_cells.volatile_get_by_hash(hash)) && (was_uncontended = cell.cas_computed {|current_value| current_value + x}) - retry_update(x, hash, was_uncontended) {|current_value| current_value + x} - end - end - end - - def increment - add(1) - end - - def decrement - add(-1) - end - - # Returns the current sum. The returned value is _NOT_ an - # atomic snapshot: Invocation in the absence of concurrent - # updates returns an accurate result, but concurrent updates that - # occur while the sum is being calculated might not be - # incorporated. - def sum - x = base - if current_cells = cells - current_cells.each do |cell| - x += cell.value if cell - end - end - x - end - - def reset - internal_reset(0) - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/cheap_lockable.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -require 'concurrent/thread_safe/util' -require 'concurrent/thread_safe/util/volatile' - -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+ - # with the +ConditionVariable+ bundled in. - # - # Usage: - # class A - # include CheapLockable - # - # def do_exlusively - # cheap_synchronize { yield } - # end - # - # def wait_for_something - # cheap_synchronize do - # cheap_wait until resource_available? - # do_something - # cheap_broadcast # wake up others - # end - # end - # end - # - # @!visibility private - module CheapLockable - private - engine = defined?(RUBY_ENGINE) && RUBY_ENGINE - if engine == 'rbx' - # Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects. - def cheap_synchronize - Rubinius.lock(self) - begin - yield - ensure - Rubinius.unlock(self) - end - end - - def cheap_wait - wchan = Rubinius::Channel.new - - begin - waiters = @waiters ||= [] - waiters.push wchan - Rubinius.unlock(self) - signaled = wchan.receive_timeout nil - ensure - Rubinius.lock(self) - - unless signaled or waiters.delete(wchan) - # we timed out, but got signaled afterwards (e.g. while waiting to - # acquire @lock), so pass that signal on to the next waiter - waiters.shift << true unless waiters.empty? - end - end - - self - end - - def cheap_broadcast - waiters = @waiters ||= [] - waiters.shift << true until waiters.empty? - self - end - elsif engine == 'jruby' - # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects - require 'jruby' - - def cheap_synchronize - JRuby.reference0(self).synchronized { yield } - end - - def cheap_wait - JRuby.reference0(self).wait - end - - def cheap_broadcast - JRuby.reference0(self).notify_all - end - else - require 'thread' - - extend Volatile - attr_volatile :mutex - - # Non-reentrant Mutex#syncrhonize - def cheap_synchronize - true until (my_mutex = mutex) || cas_mutex(nil, my_mutex = Mutex.new) - my_mutex.synchronize { yield } - end - - # Releases this object's +cheap_synchronize+ lock and goes to sleep waiting for other threads to +cheap_broadcast+, reacquires the lock on wakeup. - # Must only be called in +cheap_broadcast+'s block. - def cheap_wait - conditional_variable = @conditional_variable ||= ConditionVariable.new - conditional_variable.wait(mutex) - end - - # Wakes up all threads waiting for this object's +cheap_synchronize+ lock. - # Must only be called in +cheap_broadcast+'s block. - def cheap_broadcast - if conditional_variable = @conditional_variable - conditional_variable.broadcast - end - end - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/data_structures.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -require 'concurrent/thread_safe/util' - -# Shim for TruffleRuby.synchronized -if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized) - module TruffleRuby - def self.synchronized(object, &block) - Truffle::System.synchronized(object, &block) - end - end -end - -module Concurrent - module ThreadSafe - module Util - def self.make_synchronized_on_rbx(klass) - klass.class_eval do - private - - def _mon_initialize - @_monitor = Monitor.new unless @_monitor # avoid double initialisation - end - - def self.new(*args) - obj = super(*args) - obj.send(:_mon_initialize) - obj - end - end - - klass.superclass.instance_methods(false).each do |method| - case method - when :new_range, :new_reserved - klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{method}(*args) - obj = super - obj.send(:_mon_initialize) - obj - end - RUBY - else - klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{method}(*args) - monitor = @_monitor - monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.") - monitor.synchronize { super } - end - RUBY - end - end - end - - def self.make_synchronized_on_truffleruby(klass) - klass.superclass.instance_methods(false).each do |method| - klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{method}(*args, &block) - TruffleRuby.synchronized(self) { super(*args, &block) } - end - RUBY - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/power_of_two_tuple.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -require 'concurrent/thread_safe/util' -require 'concurrent/tuple' - -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # @!visibility private - class PowerOfTwoTuple < Concurrent::Tuple - - def initialize(size) - raise ArgumentError, "size must be a power of 2 (#{size.inspect} provided)" unless size > 0 && size & (size - 1) == 0 - super(size) - end - - def hash_to_index(hash) - (size - 1) & hash - end - - def volatile_get_by_hash(hash) - volatile_get(hash_to_index(hash)) - end - - def volatile_set_by_hash(hash, value) - volatile_set(hash_to_index(hash), value) - end - - def next_in_size_table - self.class.new(size << 1) - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/striped64.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,246 +0,0 @@ -require 'concurrent/thread_safe/util' -require 'concurrent/thread_safe/util/power_of_two_tuple' -require 'concurrent/thread_safe/util/volatile' -require 'concurrent/thread_safe/util/xor_shift_random' - -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # A Ruby port of the Doug Lea's jsr166e.Striped64 class version 1.6 - # available in public domain. - # - # Original source code available here: - # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.6 - # - # Class holding common representation and mechanics for classes supporting - # dynamic striping on 64bit values. - # - # This class maintains a lazily-initialized table of atomically updated - # variables, plus an extra +base+ field. The table size is a power of two. - # Indexing uses masked per-thread hash codes. Nearly all methods on this - # class are private, accessed directly by subclasses. - # - # Table entries are of class +Cell+; a variant of AtomicLong padded to - # reduce cache contention on most processors. Padding is overkill for most - # Atomics because they are usually irregularly scattered in memory and thus - # don't interfere much with each other. But Atomic objects residing in - # arrays will tend to be placed adjacent to each other, and so will most - # often share cache lines (with a huge negative performance impact) without - # this precaution. - # - # In part because +Cell+s are relatively large, we avoid creating them until - # they are needed. When there is no contention, all updates are made to the - # +base+ field. Upon first contention (a failed CAS on +base+ update), the - # table is initialized to size 2. The table size is doubled upon further - # contention until reaching the nearest power of two greater than or equal - # to the number of CPUS. Table slots remain empty (+nil+) until they are - # needed. - # - # A single spinlock (+busy+) is used for initializing and resizing the - # table, as well as populating slots with new +Cell+s. There is no need for - # a blocking lock: When the lock is not available, threads try other slots - # (or the base). During these retries, there is increased contention and - # reduced locality, which is still better than alternatives. - # - # Per-thread hash codes are initialized to random values. Contention and/or - # table collisions are indicated by failed CASes when performing an update - # operation (see method +retry_update+). Upon a collision, if the table size - # is less than the capacity, it is doubled in size unless some other thread - # holds the lock. If a hashed slot is empty, and lock is available, a new - # +Cell+ is created. Otherwise, if the slot exists, a CAS is tried. Retries - # proceed by "double hashing", using a secondary hash (XorShift) to try to - # find a free slot. - # - # The table size is capped because, when there are more threads than CPUs, - # supposing that each thread were bound to a CPU, there would exist a - # perfect hash function mapping threads to slots that eliminates collisions. - # When we reach capacity, we search for this mapping by randomly varying the - # hash codes of colliding threads. Because search is random, and collisions - # only become known via CAS failures, convergence can be slow, and because - # threads are typically not bound to CPUS forever, may not occur at all. - # However, despite these limitations, observed contention rates are - # typically low in these cases. - # - # It is possible for a +Cell+ to become unused when threads that once hashed - # to it terminate, as well as in the case where doubling the table causes no - # thread to hash to it under expanded mask. We do not try to detect or - # remove such cells, under the assumption that for long-running instances, - # observed contention levels will recur, so the cells will eventually be - # needed again; and for short-lived ones, it does not matter. - # - # @!visibility private - class Striped64 - - # Padded variant of AtomicLong supporting only raw accesses plus CAS. - # The +value+ field is placed between pads, hoping that the JVM doesn't - # reorder them. - # - # Optimisation note: It would be possible to use a release-only - # form of CAS here, if it were provided. - # - # @!visibility private - class Cell < Concurrent::AtomicReference - - alias_method :cas, :compare_and_set - - def cas_computed - cas(current_value = value, yield(current_value)) - end - - # @!visibility private - def self.padding - # TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot - # TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created - # hide from yardoc in a method - attr_reader *(12.times.collect{ |i| "padding_#{i}".to_sym }) - end - padding - end - - extend Volatile - attr_volatile :cells, # Table of cells. When non-null, size is a power of 2. - :base, # Base value, used mainly when there is no contention, but also as a fallback during table initialization races. Updated via CAS. - :busy # Spinlock (locked via CAS) used when resizing and/or creating Cells. - - alias_method :busy?, :busy - - def initialize - super() - self.busy = false - self.base = 0 - end - - # Handles cases of updates involving initialization, resizing, - # creating new Cells, and/or contention. See above for - # explanation. This method suffers the usual non-modularity - # problems of optimistic retry code, relying on rechecked sets of - # reads. - # - # Arguments: - # [+x+] - # the value - # [+hash_code+] - # hash code used - # [+x+] - # false if CAS failed before call - def retry_update(x, hash_code, was_uncontended) # :yields: current_value - hash = hash_code - collided = false # True if last slot nonempty - while true - if current_cells = cells - if !(cell = current_cells.volatile_get_by_hash(hash)) - if busy? - collided = false - else # Try to attach new Cell - if try_to_install_new_cell(Cell.new(x), hash) # Optimistically create and try to insert new cell - break - else - redo # Slot is now non-empty - end - end - elsif !was_uncontended # CAS already known to fail - was_uncontended = true # Continue after rehash - elsif cell.cas_computed {|current_value| yield current_value} - break - elsif current_cells.size >= CPU_COUNT || cells != current_cells # At max size or stale - collided = false - elsif collided && expand_table_unless_stale(current_cells) - collided = false - redo # Retry with expanded table - else - collided = true - end - hash = XorShiftRandom.xorshift(hash) - - elsif try_initialize_cells(x, hash) || cas_base_computed {|current_base| yield current_base} - break - end - end - self.hash_code = hash - end - - private - # Static per-thread hash code key. Shared across all instances to - # reduce Thread locals pollution and because adjustments due to - # collisions in one table are likely to be appropriate for - # others. - THREAD_LOCAL_KEY = "#{name}.hash_code".to_sym - - # A thread-local hash code accessor. The code is initially - # random, but may be set to a different value upon collisions. - def hash_code - Thread.current[THREAD_LOCAL_KEY] ||= XorShiftRandom.get - end - - def hash_code=(hash) - Thread.current[THREAD_LOCAL_KEY] = hash - end - - # Sets base and all +cells+ to the given value. - def internal_reset(initial_value) - current_cells = cells - self.base = initial_value - if current_cells - current_cells.each do |cell| - cell.value = initial_value if cell - end - end - end - - def cas_base_computed - cas_base(current_base = base, yield(current_base)) - end - - def free? - !busy? - end - - def try_initialize_cells(x, hash) - if free? && !cells - try_in_busy do - unless cells # Recheck under lock - new_cells = PowerOfTwoTuple.new(2) - new_cells.volatile_set_by_hash(hash, Cell.new(x)) - self.cells = new_cells - end - end - end - end - - def expand_table_unless_stale(current_cells) - try_in_busy do - if current_cells == cells # Recheck under lock - new_cells = current_cells.next_in_size_table - current_cells.each_with_index {|x, i| new_cells.volatile_set(i, x)} - self.cells = new_cells - end - end - end - - def try_to_install_new_cell(new_cell, hash) - try_in_busy do - # Recheck under lock - if (current_cells = cells) && !current_cells.volatile_get(i = current_cells.hash_to_index(hash)) - current_cells.volatile_set(i, new_cell) - end - end - end - - def try_in_busy - if cas_busy(false, true) - begin - yield - ensure - self.busy = false - end - end - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/volatile.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -require 'concurrent/thread_safe/util' - -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # @!visibility private - module Volatile - - # Provides +volatile+ (in the JVM's sense) attribute accessors implemented - # atop of +Concurrent::AtomicReference+. - # - # Usage: - # class Foo - # extend Concurrent::ThreadSafe::Util::Volatile - # attr_volatile :foo, :bar - # - # def initialize(bar) - # super() # must super() into parent initializers before using the volatile attribute accessors - # self.bar = bar - # end - # - # def hello - # my_foo = foo # volatile read - # self.foo = 1 # volatile write - # cas_foo(1, 2) # => true | a strong CAS - # end - # end - def attr_volatile(*attr_names) - return if attr_names.empty? - include(Module.new do - atomic_ref_setup = attr_names.map {|attr_name| "@__#{attr_name} = Concurrent::AtomicReference.new"} - initialize_copy_setup = attr_names.zip(atomic_ref_setup).map do |attr_name, ref_setup| - "#{ref_setup}(other.instance_variable_get(:@__#{attr_name}).get)" - end - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def initialize(*) - super - #{atomic_ref_setup.join('; ')} - end - - def initialize_copy(other) - super - #{initialize_copy_setup.join('; ')} - end - RUBY_EVAL - - attr_names.each do |attr_name| - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{attr_name} - @__#{attr_name}.get - end - - def #{attr_name}=(value) - @__#{attr_name}.set(value) - end - - def compare_and_set_#{attr_name}(old_value, new_value) - @__#{attr_name}.compare_and_set(old_value, new_value) - end - RUBY_EVAL - - alias_method :"cas_#{attr_name}", :"compare_and_set_#{attr_name}" - alias_method :"lazy_set_#{attr_name}", :"#{attr_name}=" - end - end) - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util/xor_shift_random.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -require 'concurrent/thread_safe/util' - -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # A xorshift random number (positive +Fixnum+s) generator, provides - # reasonably cheap way to generate thread local random numbers without - # contending for the global +Kernel.rand+. - # - # Usage: - # x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed - # while true - # if (x = XorShiftRandom.xorshift).odd? # thread-localy generate a next random number - # do_something_at_random - # end - # end - module XorShiftRandom - extend self - MAX_XOR_SHIFTABLE_INT = MAX_INT - 1 - - # Generates an initial non-zero positive +Fixnum+ via +Kernel.rand+. - def get - Kernel.rand(MAX_XOR_SHIFTABLE_INT) + 1 # 0 can't be xorshifted - end - - # xorshift based on: http://www.jstatsoft.org/v08/i14/paper - if 0.size == 4 - # using the "yˆ=y>>a; yˆ=y<>c;" transform with the (a,b,c) tuple with values (3,1,14) to minimise Bignum overflows - def xorshift(x) - x ^= x >> 3 - x ^= (x << 1) & MAX_INT # cut-off Bignum overflow - x ^= x >> 14 - end - else - # using the "yˆ=y>>a; yˆ=y<>c;" transform with the (a,b,c) tuple with values (1,1,54) to minimise Bignum overflows - def xorshift(x) - x ^= x >> 1 - x ^= (x << 1) & MAX_INT # cut-off Bignum overflow - x ^= x >> 54 - end - end - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/thread_safe/util.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -module Concurrent - - # @!visibility private - module ThreadSafe - - # @!visibility private - module Util - - # TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger - FIXNUM_BIT_SIZE = (0.size * 8) - 2 - MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1 - # TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter - CPU_COUNT = 16 # is there a way to determine this? - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/timer_task.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,334 +0,0 @@ -require 'concurrent/collection/copy_on_notify_observer_set' -require 'concurrent/concern/dereferenceable' -require 'concurrent/concern/observable' -require 'concurrent/atomic/atomic_boolean' -require 'concurrent/executor/executor_service' -require 'concurrent/executor/ruby_executor_service' -require 'concurrent/executor/safe_task_executor' -require 'concurrent/scheduled_task' - -module Concurrent - - # A very common concurrency pattern is to run a thread that performs a task at - # regular intervals. The thread that performs the task sleeps for the given - # interval then wakes up and performs the task. Lather, rinse, repeat... This - # pattern causes two problems. First, it is difficult to test the business - # logic of the task because the task itself is tightly coupled with the - # concurrency logic. Second, an exception raised while performing the task can - # cause the entire thread to abend. In a long-running application where the - # task thread is intended to run for days/weeks/years a crashed task thread - # can pose a significant problem. `TimerTask` alleviates both problems. - # - # When a `TimerTask` is launched it starts a thread for monitoring the - # execution interval. The `TimerTask` thread does not perform the task, - # however. Instead, the TimerTask launches the task on a separate thread. - # Should the task experience an unrecoverable crash only the task thread will - # crash. This makes the `TimerTask` very fault tolerant. Additionally, the - # `TimerTask` thread can respond to the success or failure of the task, - # performing logging or ancillary operations. `TimerTask` can also be - # configured with a timeout value allowing it to kill a task that runs too - # long. - # - # One other advantage of `TimerTask` is that it forces the business logic to - # be completely decoupled from the concurrency logic. The business logic can - # be tested separately then passed to the `TimerTask` for scheduling and - # running. - # - # In some cases it may be necessary for a `TimerTask` to affect its own - # execution cycle. To facilitate this, a reference to the TimerTask instance - # is passed as an argument to the provided block every time the task is - # executed. - # - # The `TimerTask` class includes the `Dereferenceable` mixin module so the - # result of the last execution is always available via the `#value` method. - # Dereferencing options can be passed to the `TimerTask` during construction or - # at any later time using the `#set_deref_options` method. - # - # `TimerTask` supports notification through the Ruby standard library - # {http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html - # Observable} module. On execution the `TimerTask` will notify the observers - # with three arguments: time of execution, the result of the block (or nil on - # failure), and any raised exceptions (or nil on success). If the timeout - # interval is exceeded the observer will receive a `Concurrent::TimeoutError` - # object as the third argument. - # - # @!macro copy_options - # - # @example Basic usage - # task = Concurrent::TimerTask.new{ puts 'Boom!' } - # task.execute - # - # task.execution_interval #=> 60 (default) - # task.timeout_interval #=> 30 (default) - # - # # wait 60 seconds... - # #=> 'Boom!' - # - # task.shutdown #=> true - # - # @example Configuring `:execution_interval` and `:timeout_interval` - # task = Concurrent::TimerTask.new(execution_interval: 5, timeout_interval: 5) do - # puts 'Boom!' - # end - # - # task.execution_interval #=> 5 - # task.timeout_interval #=> 5 - # - # @example Immediate execution with `:run_now` - # task = Concurrent::TimerTask.new(run_now: true){ puts 'Boom!' } - # task.execute - # - # #=> 'Boom!' - # - # @example Last `#value` and `Dereferenceable` mixin - # task = Concurrent::TimerTask.new( - # dup_on_deref: true, - # execution_interval: 5 - # ){ Time.now } - # - # task.execute - # Time.now #=> 2013-11-07 18:06:50 -0500 - # sleep(10) - # task.value #=> 2013-11-07 18:06:55 -0500 - # - # @example Controlling execution from within the block - # timer_task = Concurrent::TimerTask.new(execution_interval: 1) do |task| - # task.execution_interval.times{ print 'Boom! ' } - # print "\n" - # task.execution_interval += 1 - # if task.execution_interval > 5 - # puts 'Stopping...' - # task.shutdown - # end - # end - # - # timer_task.execute # blocking call - this task will stop itself - # #=> Boom! - # #=> Boom! Boom! - # #=> Boom! Boom! Boom! - # #=> Boom! Boom! Boom! Boom! - # #=> Boom! Boom! Boom! Boom! Boom! - # #=> Stopping... - # - # @example Observation - # class TaskObserver - # def update(time, result, ex) - # if result - # print "(#{time}) Execution successfully returned #{result}\n" - # elsif ex.is_a?(Concurrent::TimeoutError) - # print "(#{time}) Execution timed out\n" - # else - # print "(#{time}) Execution failed with error #{ex}\n" - # end - # end - # end - # - # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 } - # task.add_observer(TaskObserver.new) - # task.execute - # sleep 4 - # - # #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42 - # #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42 - # #=> (2013-10-13 19:09:00 -0400) Execution successfully returned 42 - # task.shutdown - # - # task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ sleep } - # task.add_observer(TaskObserver.new) - # task.execute - # - # #=> (2013-10-13 19:07:25 -0400) Execution timed out - # #=> (2013-10-13 19:07:27 -0400) Execution timed out - # #=> (2013-10-13 19:07:29 -0400) Execution timed out - # task.shutdown - # - # task = Concurrent::TimerTask.new(execution_interval: 1){ raise StandardError } - # task.add_observer(TaskObserver.new) - # task.execute - # - # #=> (2013-10-13 19:09:37 -0400) Execution failed with error StandardError - # #=> (2013-10-13 19:09:38 -0400) Execution failed with error StandardError - # #=> (2013-10-13 19:09:39 -0400) Execution failed with error StandardError - # task.shutdown - # - # @see http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html - # @see http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html - class TimerTask < RubyExecutorService - include Concern::Dereferenceable - include Concern::Observable - - # Default `:execution_interval` in seconds. - EXECUTION_INTERVAL = 60 - - # Default `:timeout_interval` in seconds. - TIMEOUT_INTERVAL = 30 - - # Create a new TimerTask with the given task and configuration. - # - # @!macro timer_task_initialize - # @param [Hash] opts the options defining task execution. - # @option opts [Integer] :execution_interval number of seconds between - # task executions (default: EXECUTION_INTERVAL) - # @option opts [Integer] :timeout_interval number of seconds a task can - # run before it is considered to have failed (default: TIMEOUT_INTERVAL) - # @option opts [Boolean] :run_now Whether to run the task immediately - # upon instantiation or to wait until the first # execution_interval - # has passed (default: false) - # - # @!macro deref_options - # - # @raise ArgumentError when no block is given. - # - # @yield to the block after :execution_interval seconds have passed since - # the last yield - # @yieldparam task a reference to the `TimerTask` instance so that the - # block can control its own lifecycle. Necessary since `self` will - # refer to the execution context of the block rather than the running - # `TimerTask`. - # - # @return [TimerTask] the new `TimerTask` - def initialize(opts = {}, &task) - raise ArgumentError.new('no block given') unless block_given? - super - set_deref_options opts - end - - # Is the executor running? - # - # @return [Boolean] `true` when running, `false` when shutting down or shutdown - def running? - @running.true? - end - - # Execute a previously created `TimerTask`. - # - # @return [TimerTask] a reference to `self` - # - # @example Instance and execute in separate steps - # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" } - # task.running? #=> false - # task.execute - # task.running? #=> true - # - # @example Instance and execute in one line - # task = Concurrent::TimerTask.new(execution_interval: 10){ print "Hello World\n" }.execute - # task.running? #=> true - def execute - synchronize do - if @running.false? - @running.make_true - schedule_next_task(@run_now ? 0 : @execution_interval) - end - end - self - end - - # Create and execute a new `TimerTask`. - # - # @!macro timer_task_initialize - # - # @example - # task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" } - # task.running? #=> true - def self.execute(opts = {}, &task) - TimerTask.new(opts, &task).execute - end - - # @!attribute [rw] execution_interval - # @return [Fixnum] Number of seconds after the task completes before the - # task is performed again. - def execution_interval - synchronize { @execution_interval } - end - - # @!attribute [rw] execution_interval - # @return [Fixnum] Number of seconds after the task completes before the - # task is performed again. - def execution_interval=(value) - if (value = value.to_f) <= 0.0 - raise ArgumentError.new('must be greater than zero') - else - synchronize { @execution_interval = value } - end - end - - # @!attribute [rw] timeout_interval - # @return [Fixnum] Number of seconds the task can run before it is - # considered to have failed. - def timeout_interval - synchronize { @timeout_interval } - end - - # @!attribute [rw] timeout_interval - # @return [Fixnum] Number of seconds the task can run before it is - # considered to have failed. - def timeout_interval=(value) - if (value = value.to_f) <= 0.0 - raise ArgumentError.new('must be greater than zero') - else - synchronize { @timeout_interval = value } - end - end - - private :post, :<< - - private - - def ns_initialize(opts, &task) - set_deref_options(opts) - - self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL - self.timeout_interval = opts[:timeout] || opts[:timeout_interval] || TIMEOUT_INTERVAL - @run_now = opts[:now] || opts[:run_now] - @executor = Concurrent::SafeTaskExecutor.new(task) - @running = Concurrent::AtomicBoolean.new(false) - @value = nil - - self.observers = Collection::CopyOnNotifyObserverSet.new - end - - # @!visibility private - def ns_shutdown_execution - @running.make_false - super - end - - # @!visibility private - def ns_kill_execution - @running.make_false - super - end - - # @!visibility private - def schedule_next_task(interval = execution_interval) - ScheduledTask.execute(interval, args: [Concurrent::Event.new], &method(:execute_task)) - nil - end - - # @!visibility private - def execute_task(completion) - return nil unless @running.true? - ScheduledTask.execute(timeout_interval, args: [completion], &method(:timeout_task)) - _success, value, reason = @executor.execute(self) - if completion.try? - self.value = value - schedule_next_task - time = Time.now - observers.notify_observers do - [time, self.value, reason] - end - end - nil - end - - # @!visibility private - def timeout_task(completion) - return unless @running.true? - if completion.try? - self.value = value - schedule_next_task - observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new) - end - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tuple.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -require 'concurrent/atomic/atomic_reference' - -module Concurrent - - # A fixed size array with volatile (synchronized, thread safe) getters/setters. - # Mixes in Ruby's `Enumerable` module for enhanced search, sort, and traversal. - # - # @example - # tuple = Concurrent::Tuple.new(16) - # - # tuple.set(0, :foo) #=> :foo | volatile write - # tuple.get(0) #=> :foo | volatile read - # tuple.compare_and_set(0, :foo, :bar) #=> true | strong CAS - # tuple.cas(0, :foo, :baz) #=> false | strong CAS - # tuple.get(0) #=> :bar | volatile read - # - # @see https://en.wikipedia.org/wiki/Tuple Tuple entry at Wikipedia - # @see http://www.erlang.org/doc/reference_manual/data_types.html#id70396 Erlang Tuple - # @see http://ruby-doc.org/core-2.2.2/Enumerable.html Enumerable - class Tuple - include Enumerable - - # The (fixed) size of the tuple. - attr_reader :size - - # @!visibility private - Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array - private_constant :Tuple - - # Create a new tuple of the given size. - # - # @param [Integer] size the number of elements in the tuple - def initialize(size) - @size = size - @tuple = tuple = Tuple.new(size) - i = 0 - while i < size - tuple[i] = Concurrent::AtomicReference.new - i += 1 - end - end - - # Get the value of the element at the given index. - # - # @param [Integer] i the index from which to retrieve the value - # @return [Object] the value at the given index or nil if the index is out of bounds - def get(i) - return nil if i >= @size || i < 0 - @tuple[i].get - end - alias_method :volatile_get, :get - - # Set the element at the given index to the given value - # - # @param [Integer] i the index for the element to set - # @param [Object] value the value to set at the given index - # - # @return [Object] the new value of the element at the given index or nil if the index is out of bounds - def set(i, value) - return nil if i >= @size || i < 0 - @tuple[i].set(value) - end - alias_method :volatile_set, :set - - # Set the value at the given index to the new value if and only if the current - # value matches the given old value. - # - # @param [Integer] i the index for the element to set - # @param [Object] old_value the value to compare against the current value - # @param [Object] new_value the value to set at the given index - # - # @return [Boolean] true if the value at the given element was set else false - def compare_and_set(i, old_value, new_value) - return false if i >= @size || i < 0 - @tuple[i].compare_and_set(old_value, new_value) - end - alias_method :cas, :compare_and_set - - # Calls the given block once for each element in self, passing that element as a parameter. - # - # @yieldparam [Object] ref the `Concurrent::AtomicReference` object at the current index - def each - @tuple.each {|ref| yield ref.get} - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/tvar.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,258 +0,0 @@ -require 'set' -require 'concurrent/synchronization' - -module Concurrent - - # A `TVar` is a transactional variable - a single-element container that - # is used as part of a transaction - see `Concurrent::atomically`. - # - # @!macro thread_safe_variable_comparison - # - # {include:file:docs-source/tvar.md} - class TVar < Synchronization::Object - safe_initialization! - - # Create a new `TVar` with an initial value. - def initialize(value) - @value = value - @version = 0 - @lock = Mutex.new - end - - # Get the value of a `TVar`. - def value - Concurrent::atomically do - Transaction::current.read(self) - end - end - - # Set the value of a `TVar`. - def value=(value) - Concurrent::atomically do - Transaction::current.write(self, value) - end - end - - # @!visibility private - def unsafe_value # :nodoc: - @value - end - - # @!visibility private - def unsafe_value=(value) # :nodoc: - @value = value - end - - # @!visibility private - def unsafe_version # :nodoc: - @version - end - - # @!visibility private - def unsafe_increment_version # :nodoc: - @version += 1 - end - - # @!visibility private - def unsafe_lock # :nodoc: - @lock - end - - end - - # Run a block that reads and writes `TVar`s as a single atomic transaction. - # With respect to the value of `TVar` objects, the transaction is atomic, in - # that it either happens or it does not, consistent, in that the `TVar` - # objects involved will never enter an illegal state, and isolated, in that - # transactions never interfere with each other. You may recognise these - # properties from database transactions. - # - # There are some very important and unusual semantics that you must be aware of: - # - # * Most importantly, the block that you pass to atomically may be executed - # more than once. In most cases your code should be free of - # side-effects, except for via TVar. - # - # * If an exception escapes an atomically block it will abort the transaction. - # - # * It is undefined behaviour to use callcc or Fiber with atomically. - # - # * If you create a new thread within an atomically, it will not be part of - # the transaction. Creating a thread counts as a side-effect. - # - # Transactions within transactions are flattened to a single transaction. - # - # @example - # a = new TVar(100_000) - # b = new TVar(100) - # - # Concurrent::atomically do - # a.value -= 10 - # b.value += 10 - # end - def atomically - raise ArgumentError.new('no block given') unless block_given? - - # Get the current transaction - - transaction = Transaction::current - - # Are we not already in a transaction (not nested)? - - if transaction.nil? - # New transaction - - begin - # Retry loop - - loop do - - # Create a new transaction - - transaction = Transaction.new - Transaction::current = transaction - - # Run the block, aborting on exceptions - - begin - result = yield - rescue Transaction::AbortError => e - transaction.abort - result = Transaction::ABORTED - rescue Transaction::LeaveError => e - transaction.abort - break result - rescue => e - transaction.abort - raise e - end - # If we can commit, break out of the loop - - if result != Transaction::ABORTED - if transaction.commit - break result - end - end - end - ensure - # Clear the current transaction - - Transaction::current = nil - end - else - # Nested transaction - flatten it and just run the block - - yield - end - end - - # Abort a currently running transaction - see `Concurrent::atomically`. - def abort_transaction - raise Transaction::AbortError.new - end - - # Leave a transaction without committing or aborting - see `Concurrent::atomically`. - def leave_transaction - raise Transaction::LeaveError.new - end - - module_function :atomically, :abort_transaction, :leave_transaction - - private - - class Transaction - - ABORTED = ::Object.new - - ReadLogEntry = Struct.new(:tvar, :version) - - AbortError = Class.new(StandardError) - LeaveError = Class.new(StandardError) - - def initialize - @read_log = [] - @write_log = {} - end - - def read(tvar) - Concurrent::abort_transaction unless valid? - - if @write_log.has_key? tvar - @write_log[tvar] - else - @read_log.push(ReadLogEntry.new(tvar, tvar.unsafe_version)) - tvar.unsafe_value - end - end - - def write(tvar, value) - # Have we already written to this TVar? - - unless @write_log.has_key? tvar - # Try to lock the TVar - - unless tvar.unsafe_lock.try_lock - # Someone else is writing to this TVar - abort - Concurrent::abort_transaction - end - - # If we previously wrote to it, check the version hasn't changed - - @read_log.each do |log_entry| - if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version - Concurrent::abort_transaction - end - end - end - - # Record the value written - - @write_log[tvar] = value - end - - def abort - unlock - end - - def commit - return false unless valid? - - @write_log.each_pair do |tvar, value| - tvar.unsafe_value = value - tvar.unsafe_increment_version - end - - unlock - - true - end - - def valid? - @read_log.each do |log_entry| - unless @write_log.has_key? log_entry.tvar - if log_entry.tvar.unsafe_version > log_entry.version - return false - end - end - end - - true - end - - def unlock - @write_log.each_key do |tvar| - tvar.unsafe_lock.unlock - end - end - - def self.current - Thread.current[:current_tvar_transaction] - end - - def self.current=(transaction) - Thread.current[:current_tvar_transaction] = transaction - end - - end - -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/at_exit.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -require 'logger' -require 'concurrent/synchronization' - -module Concurrent - - # Provides ability to add and remove handlers to be run at `Kernel#at_exit`, order is undefined. - # Each handler is executed at most once. - # - # @!visibility private - class AtExitImplementation < Synchronization::LockableObject - include Logger::Severity - - def initialize(*args) - super() - synchronize { ns_initialize(*args) } - end - - # Add a handler to be run at `Kernel#at_exit` - # @param [Object] handler_id optionally provide an id, if already present, handler is replaced - # @yield the handler - # @return id of the handler - def add(handler_id = nil, &handler) - id = handler_id || handler.object_id - synchronize { @handlers[id] = handler } - id - end - - # Delete a handler by handler_id - # @return [true, false] - def delete(handler_id) - !!synchronize { @handlers.delete handler_id } - end - - # Is handler with handler_id rpesent? - # @return [true, false] - def handler?(handler_id) - synchronize { @handlers.key? handler_id } - end - - # @return copy of the handlers - def handlers - synchronize { @handlers }.clone - end - - # install `Kernel#at_exit` callback to execute added handlers - def install - synchronize do - @installed ||= begin - at_exit { runner } - true - end - self - end - end - - # Will it run during `Kernel#at_exit` - def enabled? - synchronize { @enabled } - end - - # Configure if it runs during `Kernel#at_exit` - def enabled=(value) - synchronize { @enabled = value } - end - - # run the handlers manually - # @return ids of the handlers - def run - handlers, _ = synchronize { handlers, @handlers = @handlers, {} } - handlers.each do |_, handler| - begin - handler.call - rescue => error - Concurrent.global_logger.call(ERROR, error) - end - end - handlers.keys - end - - private - - def ns_initialize(enabled = true) - @handlers = {} - @enabled = enabled - end - - def runner - run if synchronize { @enabled } - end - end - - private_constant :AtExitImplementation - - # @see AtExitImplementation - # @!visibility private - AtExit = AtExitImplementation.new.install -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/engine.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -module Concurrent - module Utility - - # @!visibility private - module EngineDetector - def on_jruby? - ruby_engine == 'jruby' - end - - def on_jruby_9000? - on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0) - end - - def on_cruby? - ruby_engine == 'ruby' - end - - def on_rbx? - ruby_engine == 'rbx' - end - - def on_truffleruby? - ruby_engine == 'truffleruby' - end - - def on_windows? - !(RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/).nil? - end - - def on_osx? - !(RbConfig::CONFIG['host_os'] =~ /darwin|mac os/).nil? - end - - def on_linux? - !(RbConfig::CONFIG['host_os'] =~ /linux/).nil? - end - - def ruby_engine - defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - end - - def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch) - result = (version.split('.').map(&:to_i) <=> [major, minor, patch]) - comparisons = { :== => [0], - :>= => [1, 0], - :<= => [-1, 0], - :> => [1], - :< => [-1] } - comparisons.fetch(comparison).include? result - end - end - end - - # @!visibility private - extend Utility::EngineDetector -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/monotonic_time.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -require 'concurrent/synchronization' - -module Concurrent - - class_definition = Class.new(Synchronization::LockableObject) do - def initialize - @last_time = Time.now.to_f - super() - end - - if defined?(Process::CLOCK_MONOTONIC) - # @!visibility private - def get_time - Process.clock_gettime(Process::CLOCK_MONOTONIC) - end - elsif Concurrent.on_jruby? - # @!visibility private - def get_time - java.lang.System.nanoTime() / 1_000_000_000.0 - end - else - - # @!visibility private - def get_time - synchronize do - now = Time.now.to_f - if @last_time < now - @last_time = now - else # clock has moved back in time - @last_time += 0.000_001 - end - end - end - - end - end - - # Clock that cannot be set and represents monotonic time since - # some unspecified starting point. - # - # @!visibility private - GLOBAL_MONOTONIC_CLOCK = class_definition.new - private_constant :GLOBAL_MONOTONIC_CLOCK - - # @!macro monotonic_get_time - # - # Returns the current time a tracked by the application monotonic clock. - # - # @return [Float] The current monotonic time since some unspecified - # starting point - # - # @!macro monotonic_clock_warning - def monotonic_time - GLOBAL_MONOTONIC_CLOCK.get_time - end - - module_function :monotonic_time -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_extension_loader.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -require 'concurrent/utility/engine' - -module Concurrent - - module Utility - - # @!visibility private - module NativeExtensionLoader - - def allow_c_extensions? - Concurrent.on_cruby? - end - - def c_extensions_loaded? - defined?(@c_extensions_loaded) && @c_extensions_loaded - end - - def java_extensions_loaded? - defined?(@java_extensions_loaded) && @java_extensions_loaded - end - - def load_native_extensions - unless defined? Synchronization::AbstractObject - raise 'native_extension_loader loaded before Synchronization::AbstractObject' - end - - if Concurrent.on_cruby? && !c_extensions_loaded? - ['concurrent/concurrent_ruby_ext', - "concurrent/#{RUBY_VERSION[0..2]}/concurrent_ruby_ext" - ].each { |p| try_load_c_extension p } - end - - if Concurrent.on_jruby? && !java_extensions_loaded? - begin - require 'concurrent/concurrent_ruby.jar' - set_java_extensions_loaded - rescue LoadError => e - raise e, "Java extensions are required for JRuby.\n" + e.message, e.backtrace - end - end - end - - private - - def load_error_path(error) - if error.respond_to? :path - error.path - else - error.message.split(' -- ').last - end - end - - def set_c_extensions_loaded - @c_extensions_loaded = true - end - - def set_java_extensions_loaded - @java_extensions_loaded = true - end - - def try_load_c_extension(path) - require path - set_c_extensions_loaded - rescue LoadError => e - if load_error_path(e) == path - # move on with pure-Ruby implementations - # TODO (pitr-ch 12-Jul-2018): warning on verbose? - else - raise e - end - end - - end - end - - # @!visibility private - extend Utility::NativeExtensionLoader -end - diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/native_integer.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -module Concurrent - module Utility - # @private - module NativeInteger - # http://stackoverflow.com/questions/535721/ruby-max-integer - MIN_VALUE = -(2**(0.size * 8 - 2)) - MAX_VALUE = (2**(0.size * 8 - 2) - 1) - - def ensure_upper_bound(value) - if value > MAX_VALUE - raise RangeError.new("#{value} is greater than the maximum value of #{MAX_VALUE}") - end - value - end - - def ensure_lower_bound(value) - if value < MIN_VALUE - raise RangeError.new("#{value} is less than the maximum value of #{MIN_VALUE}") - end - value - end - - def ensure_integer(value) - unless value.is_a?(Integer) - raise ArgumentError.new("#{value} is not an Integer") - end - value - end - - def ensure_integer_and_bounds(value) - ensure_integer value - ensure_upper_bound value - ensure_lower_bound value - end - - def ensure_positive(value) - if value < 0 - raise ArgumentError.new("#{value} cannot be negative") - end - value - end - - def ensure_positive_and_no_zero(value) - if value < 1 - raise ArgumentError.new("#{value} cannot be negative or zero") - end - value - end - - extend self - end - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/utility/processor_counter.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -require 'rbconfig' -require 'concurrent/delay' - -module Concurrent - module Utility - - # @!visibility private - class ProcessorCounter - def initialize - @processor_count = Delay.new { compute_processor_count } - @physical_processor_count = Delay.new { compute_physical_processor_count } - end - - # Number of processors seen by the OS and used for process scheduling. For - # performance reasons the calculated value will be memoized on the first - # call. - # - # When running under JRuby the Java runtime call - # `java.lang.Runtime.getRuntime.availableProcessors` will be used. According - # to the Java documentation this "value may change during a particular - # invocation of the virtual machine... [applications] should therefore - # occasionally poll this property." Subsequently the result will NOT be - # memoized under JRuby. - # - # On Windows the Win32 API will be queried for the - # `NumberOfLogicalProcessors from Win32_Processor`. This will return the - # total number "logical processors for the current instance of the - # processor", which taked into account hyperthreading. - # - # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev - # * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used) - # * BSD: /sbin/sysctl - # * Cygwin: /proc/cpuinfo - # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl - # * HP-UX: /usr/sbin/ioscan - # * IRIX: /usr/sbin/sysconf - # * Linux: /proc/cpuinfo - # * Minix 3+: /proc/cpuinfo - # * Solaris: /usr/sbin/psrinfo - # * Tru64 UNIX: /usr/sbin/psrinfo - # * UnixWare: /usr/sbin/psrinfo - # - # @return [Integer] number of processors seen by the OS or Java runtime - # - # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb - # - # @see http://docs.oracle.com/javase/6/docs/api/java/lang/Runtime.html#availableProcessors() - # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx - def processor_count - @processor_count.value - end - - # Number of physical processor cores on the current system. For performance - # reasons the calculated value will be memoized on the first call. - # - # On Windows the Win32 API will be queried for the `NumberOfCores from - # Win32_Processor`. This will return the total number "of cores for the - # current instance of the processor." On Unix-like operating systems either - # the `hwprefs` or `sysctl` utility will be called in a subshell and the - # returned value will be used. In the rare case where none of these methods - # work or an exception is raised the function will simply return 1. - # - # @return [Integer] number physical processor cores on the current system - # - # @see https://github.com/grosser/parallel/blob/4fc8b89d08c7091fe0419ca8fba1ec3ce5a8d185/lib/parallel.rb - # - # @see http://msdn.microsoft.com/en-us/library/aa394373(v=vs.85).aspx - # @see http://www.unix.com/man-page/osx/1/HWPREFS/ - # @see http://linux.die.net/man/8/sysctl - def physical_processor_count - @physical_processor_count.value - end - - private - - def compute_processor_count - if Concurrent.on_jruby? - java.lang.Runtime.getRuntime.availableProcessors - else - os_name = RbConfig::CONFIG["target_os"] - if os_name =~ /mingw|mswin/ - require 'win32ole' - result = WIN32OLE.connect("winmgmts://").ExecQuery( - "select NumberOfLogicalProcessors from Win32_Processor") - result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+) - elsif File.readable?("/proc/cpuinfo") && (cpuinfo_count = IO.read("/proc/cpuinfo").scan(/^processor/).size) > 0 - cpuinfo_count - elsif File.executable?("/usr/bin/nproc") - IO.popen("/usr/bin/nproc --all", &:read).to_i - elsif File.executable?("/usr/bin/hwprefs") - IO.popen("/usr/bin/hwprefs thread_count", &:read).to_i - elsif File.executable?("/usr/sbin/psrinfo") - IO.popen("/usr/sbin/psrinfo", &:read).scan(/^.*on-*line/).size - elsif File.executable?("/usr/sbin/ioscan") - IO.popen("/usr/sbin/ioscan -kC processor", &:read).scan(/^.*processor/).size - elsif File.executable?("/usr/sbin/pmcycles") - IO.popen("/usr/sbin/pmcycles -m", &:read).count("\n") - elsif File.executable?("/usr/sbin/lsdev") - IO.popen("/usr/sbin/lsdev -Cc processor -S 1", &:read).count("\n") - elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i - IO.popen("/usr/sbin/sysconf NPROC_ONLN", &:read).to_i - elsif File.executable?("/usr/sbin/sysctl") - IO.popen("/usr/sbin/sysctl -n hw.ncpu", &:read).to_i - elsif File.executable?("/sbin/sysctl") - IO.popen("/sbin/sysctl -n hw.ncpu", &:read).to_i - else - # TODO (pitr-ch 05-Nov-2016): warn about failures - 1 - end - end - rescue - return 1 - end - - def compute_physical_processor_count - ppc = case RbConfig::CONFIG["target_os"] - when /darwin1/ - IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i - when /linux/ - cores = {} # unique physical ID / core ID combinations - phy = 0 - IO.read("/proc/cpuinfo").scan(/^physical id.*|^core id.*/) do |ln| - if ln.start_with?("physical") - phy = ln[/\d+/] - elsif ln.start_with?("core") - cid = phy + ":" + ln[/\d+/] - cores[cid] = true if not cores[cid] - end - end - cores.count - when /mswin|mingw/ - require 'win32ole' - result_set = WIN32OLE.connect("winmgmts://").ExecQuery( - "select NumberOfCores from Win32_Processor") - result_set.to_enum.collect(&:NumberOfCores).reduce(:+) - else - processor_count - end - # fall back to logical count if physical info is invalid - ppc > 0 ? ppc : processor_count - rescue - return 1 - end - end - end - - # create the default ProcessorCounter on load - @processor_counter = Utility::ProcessorCounter.new - singleton_class.send :attr_reader, :processor_counter - - def self.processor_count - processor_counter.processor_count - end - - def self.physical_processor_count - processor_counter.physical_processor_count - end -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent/version.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -module Concurrent - VERSION = '1.1.5' -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent-ruby.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -require_relative "./concurrent" diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent.rb puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent.rb --- puppetserver-7.9.5/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent.rb 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/gems/concurrent-ruby-1.1.5/lib/concurrent.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -require 'concurrent/version' -require 'concurrent/constants' -require 'concurrent/errors' -require 'concurrent/configuration' - -require 'concurrent/atomics' -require 'concurrent/executors' -require 'concurrent/synchronization' - -require 'concurrent/atomic/atomic_markable_reference' -require 'concurrent/atomic/atomic_reference' -require 'concurrent/agent' -require 'concurrent/atom' -require 'concurrent/array' -require 'concurrent/hash' -require 'concurrent/set' -require 'concurrent/map' -require 'concurrent/tuple' -require 'concurrent/async' -require 'concurrent/dataflow' -require 'concurrent/delay' -require 'concurrent/exchanger' -require 'concurrent/future' -require 'concurrent/immutable_struct' -require 'concurrent/ivar' -require 'concurrent/maybe' -require 'concurrent/mutable_struct' -require 'concurrent/mvar' -require 'concurrent/promise' -require 'concurrent/scheduled_task' -require 'concurrent/settable_struct' -require 'concurrent/timer_task' -require 'concurrent/tvar' -require 'concurrent/promises' - -require 'concurrent/thread_safe/synchronized_delegator' -require 'concurrent/thread_safe/util' - -require 'concurrent/options' - -# @!macro internal_implementation_note -# -# @note **Private Implementation:** This abstraction is a private, internal -# implementation detail. It should never be used directly. - -# @!macro monotonic_clock_warning -# -# @note Time calculations on all platforms and languages are sensitive to -# changes to the system clock. To alleviate the potential problems -# associated with changing the system clock while an application is running, -# most modern operating systems provide a monotonic clock that operates -# independently of the system clock. A monotonic clock cannot be used to -# determine human-friendly clock times. A monotonic clock is used exclusively -# for calculating time intervals. Not all Ruby platforms provide access to an -# operating system monotonic clock. On these platforms a pure-Ruby monotonic -# clock will be used as a fallback. An operating system monotonic clock is both -# faster and more reliable than the pure-Ruby implementation. The pure-Ruby -# implementation should be fast and reliable enough for most non-realtime -# operations. At this time the common Ruby platforms that provide access to an -# operating system monotonic clock are MRI 2.1 and above and JRuby (all versions). -# -# @see http://linux.die.net/man/3/clock_gettime Linux clock_gettime(3) - -# @!macro copy_options -# -# ## Copy Options -# -# Object references in Ruby are mutable. This can lead to serious -# problems when the {#value} of an object is a mutable reference. Which -# is always the case unless the value is a `Fixnum`, `Symbol`, or similar -# "primitive" data type. Each instance can be configured with a few -# options that can help protect the program from potentially dangerous -# operations. Each of these options can be optionally set when the object -# instance is created: -# -# * `:dup_on_deref` When true the object will call the `#dup` method on -# the `value` object every time the `#value` method is called -# (default: false) -# * `:freeze_on_deref` When true the object will call the `#freeze` -# method on the `value` object every time the `#value` method is called -# (default: false) -# * `:copy_on_deref` When given a `Proc` object the `Proc` will be run -# every time the `#value` method is called. The `Proc` will be given -# the current `value` as its only argument and the result returned by -# the block will be the return value of the `#value` call. When `nil` -# this option will be ignored (default: nil) -# -# When multiple deref options are set the order of operations is strictly defined. -# The order of deref operations is: -# * `:copy_on_deref` -# * `:dup_on_deref` -# * `:freeze_on_deref` -# -# Because of this ordering there is no need to `#freeze` an object created by a -# provided `:copy_on_deref` block. Simply set `:freeze_on_deref` to `true`. -# Setting both `:dup_on_deref` to `true` and `:freeze_on_deref` to `true` is -# as close to the behavior of a "pure" functional language (like Erlang, Clojure, -# or Haskell) as we are likely to get in Ruby. - -# @!macro deref_options -# -# @option opts [Boolean] :dup_on_deref (false) Call `#dup` before -# returning the data from {#value} -# @option opts [Boolean] :freeze_on_deref (false) Call `#freeze` before -# returning the data from {#value} -# @option opts [Proc] :copy_on_deref (nil) When calling the {#value} -# method, call the given proc passing the internal value as the sole -# argument then return the new value returned from the proc. - -# @!macro executor_and_deref_options -# -# @param [Hash] opts the options used to define the behavior at update and deref -# and to specify the executor on which to perform actions -# @option opts [Executor] :executor when set use the given `Executor` instance. -# Three special values are also supported: `:io` returns the global pool for -# long, blocking (IO) tasks, `:fast` returns the global pool for short, fast -# operations, and `:immediate` returns the global `ImmediateExecutor` object. -# @!macro deref_options - -# @!macro warn.edge -# @api Edge -# @note **Edge Features** are under active development and may change frequently. -# -# - Deprecations are not added before incompatible changes. -# - Edge version: _major_ is always 0, _minor_ bump means incompatible change, -# _patch_ bump means compatible change. -# - Edge features may also lack tests and documentation. -# - Features developed in `concurrent-ruby-edge` are expected to move -# to `concurrent-ruby` when finalised. - - -# {include:file:README.md} -module Concurrent -end diff -Nru puppetserver-7.9.5/rubygem-concurrent-ruby/specifications/concurrent-ruby-1.1.5.gemspec puppetserver-8.4.0/rubygem-concurrent-ruby/specifications/concurrent-ruby-1.1.5.gemspec --- puppetserver-7.9.5/rubygem-concurrent-ruby/specifications/concurrent-ruby-1.1.5.gemspec 2023-01-30 18:29:02.000000000 +0000 +++ puppetserver-8.4.0/rubygem-concurrent-ruby/specifications/concurrent-ruby-1.1.5.gemspec 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# -*- encoding: utf-8 -*- -# stub: concurrent-ruby 1.1.5 ruby lib - -Gem::Specification.new do |s| - s.name = "concurrent-ruby".freeze - s.version = "1.1.5" - - s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= - s.metadata = { "source_code_uri" => "https://github.com/ruby-concurrency/concurrent-ruby" } if s.respond_to? :metadata= - s.require_paths = ["lib".freeze] - s.authors = ["Jerry D'Antonio".freeze, "Petr Chalupa".freeze, "The Ruby Concurrency Team".freeze] - s.date = "2019-03-11" - s.description = "Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.\nInspired by Erlang, Clojure, Go, JavaScript, actors, and classic concurrency patterns.\n".freeze - s.email = "concurrent-ruby@googlegroups.com".freeze - s.extra_rdoc_files = ["README.md".freeze, "LICENSE.md".freeze, "CHANGELOG.md".freeze] - s.files = ["CHANGELOG.md".freeze, "LICENSE.md".freeze, "README.md".freeze] - s.homepage = "http://www.concurrent-ruby.com".freeze - s.licenses = ["MIT".freeze] - s.required_ruby_version = Gem::Requirement.new(">= 1.9.3".freeze) - s.rubygems_version = "3.2.5".freeze - s.summary = "Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.".freeze - - s.installed_by_version = "3.2.5" if s.respond_to? :installed_by_version -end diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/certificate_authority.clj puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/certificate_authority.clj --- puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/certificate_authority.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/certificate_authority.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,36 +1,95 @@ (ns puppetlabs.puppetserver.certificate-authority - (:import [org.apache.commons.io IOUtils] - [java.util Date] - [java.io InputStream ByteArrayOutputStream ByteArrayInputStream File StringReader IOException] - [java.nio.file Files Paths LinkOption] - [java.nio.file.attribute FileAttribute PosixFilePermissions] - (java.security PrivateKey PublicKey KeyPair) - (org.joda.time DateTime) - (java.security.cert CRLException CertPathValidatorException X509CRL) - (javax.security.auth.x500 X500Principal) - (sun.security.x509 X509CertImpl)) - (:require [me.raynes.fs :as fs] - [schema.core :as schema] - [clojure.string :as str] - [clojure.set :as set] - [clojure.java.io :as io] - [clojure.tools.logging :as log] + (:require [clj-time.coerce :as time-coerce] [clj-time.core :as time] [clj-time.format :as time-format] - [clj-time.coerce :as time-coerce] - [slingshot.slingshot :as sling] + [clojure.java.io :as io] + [clojure.set :as set] + [clojure.string :as str] + [clojure.tools.logging :as log] + [me.raynes.fs :as fs] + [puppetlabs.i18n.core :as i18n] [puppetlabs.kitchensink.core :as ks] [puppetlabs.kitchensink.file :as ks-file] + [puppetlabs.puppetserver.common :as common] [puppetlabs.puppetserver.ringutils :as ringutils] - [puppetlabs.ssl-utils.core :as utils] - [clojure.set :as cset :refer [union]] - [clj-yaml.core :as yaml] [puppetlabs.puppetserver.shell-utils :as shell-utils] - [puppetlabs.i18n.core :as i18n])) + [puppetlabs.ssl-utils.core :as utils] + [schema.core :as schema] + [slingshot.slingshot :as sling]) + (:import (java.io BufferedReader BufferedWriter ByteArrayInputStream ByteArrayOutputStream File FileNotFoundException IOException InputStream Reader StringReader) + (java.nio CharBuffer) + (java.nio.file Files Path Paths) + (java.nio.file.attribute FileAttribute PosixFilePermissions) + (java.security PrivateKey PublicKey) + (java.security.cert CRLException CertPathValidatorException X509CRL X509Certificate) + (java.text SimpleDateFormat) + (java.time LocalDateTime ZoneId) + (java.util Date) + (java.util.concurrent.locks ReentrantReadWriteLock) + (org.apache.commons.io IOUtils) + (org.bouncycastle.pkcs PKCS10CertificationRequest) + (org.joda.time DateTime))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Public utilities + +;; Pattern is one or more digit followed by time unit +(def digits-with-unit-pattern #"(\d+)(y|d|h|m|s)") +(def repeated-digits-with-unit-pattern #"((\d+)(y|d|h|m|s))+") + +(defn duration-string? + "Returns true if string is formatted with duration string pairs only, otherwise returns nil. + Ignores whitespace." + [maybe-duration-string] + (when (string? maybe-duration-string) + (let [no-whitespace-string (clojure.string/replace maybe-duration-string #" " "")] + (some? (re-matches repeated-digits-with-unit-pattern no-whitespace-string))))) + +(defn duration-str->sec + "Converts a string containing any combination of duration string pairs in the format 'y' 'd' 'm' 'h' 's' + to a total number of seconds. + nil is returned if the input is not a string or not a string containing any valid duration string pairs." + [string-input] + (when (duration-string? string-input) + (let [pattern-matcher (re-matcher digits-with-unit-pattern string-input) + first-match (re-find pattern-matcher)] + (loop [[_match-str digits unit] first-match + running-total 0] + (let [unit-in-seconds (case unit + "y" 31536000 ;; 365 day year, not a real year + "d" 86400 + "h" 3600 + "m" 60 + "s" 1) + total-seconds (+ running-total (* (Integer/parseInt digits) unit-in-seconds)) + next-match (re-find pattern-matcher)] + (if (some? next-match) + (recur next-match total-seconds) + total-seconds)))))) + +(defn get-ca-ttl + "Returns ca-ttl value as an integer. If a value is set in certificate-authority that value is returned. + Otherwise puppet config setting is returned" + [puppetserver certificate-authority] + (let [ca-config-value (duration-str->sec (:ca-ttl certificate-authority)) + puppet-config-value (:ca-ttl puppetserver)] + (when (and ca-config-value puppet-config-value) + (log/warn (i18n/trs "Detected ca-ttl setting in CA config which will take precedence over puppet.conf setting"))) + (or ca-config-value puppet-config-value))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Schemas +(def AutoSignInput + (schema/cond-pre schema/Bool schema/Str)) + +(def CertificateOrCSR + (schema/cond-pre X509Certificate PKCS10CertificationRequest)) + +(def TTLDuration + (schema/cond-pre schema/Int schema/Str)) + (def MasterSettings "Settings from Puppet that are necessary for SSL initialization on the master. Most of these are files and directories within the SSL directory, excluding @@ -54,6 +113,15 @@ Currently we only control access to the certificate_status(es) endpoints." {(schema/optional-key :certificate-status) ringutils/WhitelistSettings}) +(defn positive-integer? + [i] + (and (integer? i) + (pos? i))) + +(def PosInt + "Any integer z in Z where z > 0." + (schema/pred positive-integer? 'positive-integer?)) + (def CaSettings "Settings from Puppet that are necessary for CA initialization and request handling during normal Puppet operation. @@ -62,7 +130,10 @@ :allow-authorization-extensions schema/Bool :allow-duplicate-certs schema/Bool :allow-subject-alt-names schema/Bool - :autosign (schema/either schema/Str schema/Bool) + :allow-auto-renewal schema/Bool + :auto-renewal-cert-ttl TTLDuration + :allow-header-cert-info schema/Bool + :autosign AutoSignInput :cacert schema/Str :cadir schema/Str :cacrl schema/Str @@ -88,7 +159,13 @@ :infra-crl-path schema/Str ;; Option to continue using full CRL instead of infra CRL if desired ;; Infra CRL would be enabled by default. - :enable-infra-crl schema/Bool}) + :enable-infra-crl schema/Bool + :serial-lock ReentrantReadWriteLock + :serial-lock-timeout-seconds PosInt + :crl-lock ReentrantReadWriteLock + :crl-lock-timeout-seconds PosInt + :inventory-lock ReentrantReadWriteLock + :inventory-lock-timeout-seconds PosInt}) (def DesiredCertificateState "The pair of states that may be submitted to the certificate @@ -140,9 +217,6 @@ (def CertificateRevocationList (schema/pred utils/certificate-revocation-list?)) -(def CertificateRequest - (schema/pred utils/certificate-request?)) - (def OutcomeInfo "Generic map of outcome & message for API consumers" {:outcome (schema/enum :success :not-found :error) @@ -157,6 +231,25 @@ (def default-allow-auth-extensions false) +(def default-serial-lock-timeout-seconds + 5) + +(def default-crl-lock-timeout-seconds + ;; for large crls, and a slow disk a longer timeout is needed + 60) + +(def default-inventory-lock-timeout-seconds + 60) + +(def default-auto-ttl-renewal + "90d") ; 90 days by default + +(def default-auto-ttl-renewal-seconds + (duration-str->sec default-auto-ttl-renewal)) ; 90 days by default + +;; if the crl is going to expire in less than this number of days, it should be regenerated. +(def crl-expiration-window-days 30) + (schema/defn ^:always-validate initialize-ca-config "Adds in default ca config keys/values, which may be overwritten if a value for any of those keys already exists in the ca-data" @@ -167,7 +260,13 @@ :infra-crl-path (str cadir "/infra_crl.pem") :enable-infra-crl false :allow-subject-alt-names default-allow-subj-alt-names - :allow-authorization-extensions default-allow-auth-extensions}] + :allow-authorization-extensions default-allow-auth-extensions + :serial-lock-timeout-seconds default-serial-lock-timeout-seconds + :crl-lock-timeout-seconds default-crl-lock-timeout-seconds + :inventory-lock-timeout-seconds default-inventory-lock-timeout-seconds + :allow-auto-renewal false + :auto-renewal-cert-ttl default-auto-ttl-renewal + :allow-header-cert-info false}] (merge defaults ca-data))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -243,6 +342,10 @@ :pp_auth_role "1.3.6.1.4.1.34380.1.3.13" :pp_cli_auth cli-auth-oid}) +;; OID for the attribute that indicates if the agent supports auto-renewal or not +(def pp_auth_auto_renew-attribute + "1.3.6.1.4.1.34380.1.3.2",) + (def netscape-comment-value "Standard value applied to the Netscape Comment extension for certificates" "Puppet Server Internal Certificate") @@ -250,7 +353,7 @@ (defn required-ca-files "The set of SSL related files that are required on the CA." [enable-infra-crl] - (union #{:cacert :cacrl :cakey :cert-inventory :serial} + (set/union #{:cacert :cacrl :cakey :cert-inventory :serial} (if enable-infra-crl #{:infra-nodes-path :infra-crl-path} #{}))) (def max-ca-ttl @@ -272,40 +375,13 @@ "Posix permissions for the private key directory on disk." "rwxr-x---") -(def empty-string-array - "The API for Paths/get requires a string array which is empty for all of the - needs of this namespace. " - (into-array String [])) - -(defn get-path-obj - "Create a Path object from the provided path." - [path] - (Paths/get path empty-string-array)) - -(schema/defn set-file-perms :- File - "Set the provided permissions on the given path. The permissions string is in - the form of the standard 9 character posix format, ie \"rwxr-xr-x\"." - [path :- schema/Str - permissions :- schema/Str] - (-> (get-path-obj path) - (Files/setPosixFilePermissions - (PosixFilePermissions/fromString permissions)) - (.toFile))) - -(schema/defn get-file-perms :- schema/Str - "Returns the currently set permissions of the given file path." - [path :- schema/Str] - (-> (get-path-obj path) - (Files/getPosixFilePermissions (into-array LinkOption [])) - PosixFilePermissions/toString)) - (schema/defn create-file-with-perms :- File "Create a new empty file which has the provided posix file permissions. The permissions string is in the form of the standard 9 character posix format. " [path :- schema/Str permissions :- schema/Str] (let [perms-set (PosixFilePermissions/fromString permissions)] - (-> (get-path-obj path) + (-> (ks-file/str->path path) (Files/createFile (into-array FileAttribute [(PosixFilePermissions/asFileAttribute perms-set)])) @@ -330,20 +406,31 @@ These paths are necessary during CA initialization for determining what needs to be created and where they should be placed." [ca-settings :- CaSettings] - (-> (dissoc ca-settings - :access-control - :allow-authorization-extensions - :allow-duplicate-certs - :allow-subject-alt-names - :autosign - :ca-name - :ca-ttl - :keylength - :manage-internal-file-permissions - :ruby-load-path - :gem-path - :enable-infra-crl) - (dissoc (when-not (:enable-infra-crl ca-settings) :infra-crl-path :infra-node-serials-path)))) + (let [settings' (dissoc ca-settings + :access-control + :allow-authorization-extensions + :allow-duplicate-certs + :allow-subject-alt-names + :autosign + :ca-name + :ca-ttl + :allow-header-cert-info + :keylength + :manage-internal-file-permissions + :ruby-load-path + :gem-path + :enable-infra-crl + :serial-lock-timeout-seconds + :serial-lock + :crl-lock-timeout-seconds + :crl-lock + :inventory-lock + :inventory-lock-timeout-seconds + :allow-auto-renewal + :auto-renewal-cert-ttl)] + (if (:enable-infra-crl ca-settings) + settings' + (dissoc settings' :infra-crl-path :infra-node-serials-path)))) (schema/defn settings->ssldir-paths "Remove all keys from the master settings map which are not file or directory @@ -406,22 +493,26 @@ (schema/defn dns-alt-names :- [schema/Str] "Get the list of DNS alt names on the provided certificate or CSR. Each name will be prepended with 'DNS:'." - [cert-or-csr :- (schema/either Certificate CertificateRequest)] + [cert-or-csr :- CertificateOrCSR] (mapv (partial str "DNS:") (utils/get-subject-dns-alt-names cert-or-csr))) (schema/defn subject-alt-names :- [schema/Str] "Get the list of both DNS and IP alt names on the provided certificate or CSR. Each name will be prepended with 'DNS:' or 'IP:'." - [cert-or-csr :- (schema/either Certificate CertificateRequest)] + [cert-or-csr :- CertificateOrCSR] (into (mapv (partial str "IP:") (utils/get-subject-ip-alt-names cert-or-csr)) (mapv (partial str "DNS:") (utils/get-subject-dns-alt-names cert-or-csr)))) +(schema/defn get-csr-attributes :- utils/SSLMultiValueAttributeList + [csr :- PKCS10CertificationRequest] + (utils/get-attributes csr)) + (schema/defn authorization-extensions :- {schema/Str schema/Str} "Get the authorization extensions for the certificate or CSR. These are extensions that fall under the ppAuthCert OID arc. Returns a map of OIDS to values." - [cert-or-csr :- (schema/either Certificate CertificateRequest)] + [cert-or-csr :- CertificateOrCSR] (let [extensions (utils/get-extensions cert-or-csr) auth-exts (filter (fn [{:keys [oid]}] (utils/subtree-of? ppAuthCertExt oid)) @@ -436,8 +527,6 @@ {} auth-exts))) -(defn seq-contains? [coll target] (some #(= target %) coll)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Writing various SSL objects safely ;; These versions all encode writing atomically and knowledge of file permissions @@ -472,9 +561,10 @@ (schema/defn write-crls "Encode a list of CRLS to PEM format and write it to a file atomically and - with appropriate permissions." + with appropriate permissions. Note, assumes proper locking is done." [crls :- [CertificateRevocationList] path :- schema/Str] + ;; use an atomic write for crash safety. (ks-file/atomic-write path (partial utils/objs->pem! crls) public-key-perms)) (schema/defn write-csr @@ -485,11 +575,19 @@ (ks-file/atomic-write path (partial utils/obj->pem! csr) public-key-perms)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Serial number functions + lock +;;; Serial number functions -(def serial-file-lock - "The lock used to prevent concurrent access to the serial number file." - (new Object)) +(def serial-lock-descriptor + "Text used in exceptions to help identify locking issues" + "serial-file") + +(def crl-lock-descriptor + "Text used in exceptions to help identify locking issues" + "crl-file") + +(def inventory-lock-descriptor + "Text used in exceptions to help identify locking issues" + "inventory-file") (schema/defn parse-serial-number :- schema/Int "Parses a serial number from its format on disk. See `format-serial-number` @@ -499,11 +597,12 @@ (schema/defn get-serial-number! :- schema/Int "Reads the serial number file from disk and returns the serial number." - [serial-file :- schema/Str] - (-> serial-file - (slurp) - (.trim) - (parse-serial-number))) + [{:keys [serial serial-lock serial-lock-timeout-seconds]} :- CaSettings] + (common/with-safe-read-lock serial-lock serial-lock-descriptor serial-lock-timeout-seconds + (-> serial + (slurp) + (.trim) + (parse-serial-number)))) (schema/defn format-serial-number :- schema/Str "Converts a serial number to the format it needs to be written in on disk. @@ -521,20 +620,21 @@ Reads the serial number as a hex value from the given file and replaces the contents of `serial-file` with the next serial number for a subsequent call. Puppet's $serial setting defines the location of the serial number file." - [serial-file :- schema/Str] - (locking serial-file-lock - (let [serial-number (get-serial-number! serial-file)] - (ks-file/atomic-write-string serial-file + [{:keys [serial serial-lock serial-lock-timeout-seconds] :as ca-settings} :- CaSettings] + (common/with-safe-write-lock serial-lock serial-lock-descriptor serial-lock-timeout-seconds + (let [serial-number (get-serial-number! ca-settings)] + (ks-file/atomic-write-string serial (format-serial-number (inc serial-number)) serial-file-permissions) serial-number))) (schema/defn initialize-serial-file! "Initializes the serial number file on disk. Serial numbers start at 1." - [path :- schema/Str] - (ks-file/atomic-write-string path - (format-serial-number 1) - serial-file-permissions)) + [{:keys [serial serial-lock serial-lock-timeout-seconds]} :- CaSettings] + (common/with-safe-write-lock serial-lock serial-lock-descriptor serial-lock-timeout-seconds + (ks-file/atomic-write-string serial + (format-serial-number 1) + serial-file-permissions))) (schema/defn write-local-cacrl! :- (schema/maybe Exception) "Spits the contents of 'cacrl-contents' string to the 'localcacrl' file @@ -560,15 +660,90 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Inventory File +(def inventory-date-formatter + (time-format/formatter "YYY-MM-dd'T'HH:mm:ssz")) + (schema/defn format-date-time :- schema/Str "Formats a date-time into the format expected by the ruby puppet code." [date-time :- Date] (time-format/unparse - (time-format/formatter "YYY-MM-dd'T'HH:mm:ssz") + inventory-date-formatter (time-coerce/from-date date-time))) +(schema/defn parse-date-time :- DateTime + "parses a date-time string into a DateTime instance" + [date-time :- schema/Str] + (time-format/parse inventory-date-formatter date-time)) + +(def buffer-copy-size (* 64 1024)) + +(schema/defn read-infra-nodes + "Returns a list of infra nodes or infra node serials from the specified file organized as one item per line." + [infra-file-reader :- Reader] + (line-seq infra-file-reader)) + +(schema/defn maybe-write-to-infra-serial! + "Determine if the host in question is an infra host, and if it is, add the provided serial number to the + infra-serials file" + [serial :- BigInteger + certname :- schema/Str + {:keys [infra-nodes-path infra-node-serials-path]} :- CaSettings] + (when (fs/exists? infra-nodes-path) + (with-open [infra-nodes-reader (io/reader infra-nodes-path)] + (let [infra-nodes (read-infra-nodes infra-nodes-reader)] + (when (seq (filter #(= certname %) infra-nodes)) + (log/debug (i18n/trs "Appending serial number {0} for {1} " serial certname)) + (let [stream-content-fn + (fn [^BufferedWriter writer] + (log/trace (i18n/trs "Begin append to serial file.")) + (let [copy-buffer (CharBuffer/allocate buffer-copy-size)] + (try + (with-open [^BufferedReader reader (io/reader infra-node-serials-path)] + ;; copy all the existing content + (loop [read-length (.read reader copy-buffer)] + ;; theoretically read can return 0, which means try again + (when (<= 0 read-length) + (when (pos? read-length) + (.write writer (.array copy-buffer) 0 read-length)) + (.clear copy-buffer) + (recur (.read reader copy-buffer))))) + (catch FileNotFoundException _e + (log/trace (i18n/trs "Inventory serial file not found. Assume empty."))) + (catch Throwable e + (log/error e (i18n/trs "Error while appending to serial file.")) + (throw e)))) + (.write writer (str serial)) + (.newLine writer) + (.flush writer) + (log/trace (i18n/trs "Finish append to serial file. ")))] + (ks-file/atomic-write infra-node-serials-path stream-content-fn))))))) + +(defn stream-content-to-file + [^String cert-inventory ^String entry ^BufferedWriter writer] + (log/trace (i18n/trs "Begin append to inventory file.")) + (let [copy-buffer (CharBuffer/allocate buffer-copy-size)] + (try + (with-open [^BufferedReader reader (io/reader cert-inventory)] + ;; copy all the existing content + (loop [read-length (.read reader copy-buffer)] + ;; theoretically read can return 0, which means try again + (when (<= 0 read-length) + (when (pos? read-length) + (.write writer (.array copy-buffer) 0 read-length)) + (.clear copy-buffer) + (recur (.read reader copy-buffer))))) + (catch FileNotFoundException _e + (log/trace (i18n/trs "Inventory file not found. Assume empty."))) + (catch Throwable e + (log/error e (i18n/trs "Error while appending to inventory file.")) + (throw e)))) + (.write writer entry) + (.flush writer) + (log/trace (i18n/trs "Finish append to inventory file. "))) + + (schema/defn ^:always-validate - write-cert-to-inventory! + write-cert-to-inventory-unlocked! "Writes an entry into Puppet's inventory file for a given certificate. The location of this file is defined by Puppet's 'cert_inventory' setting. The inventory is a text file where each line represents a certificate in the @@ -582,11 +757,11 @@ * $NA = The 'not after' field of the cert, as a date/timestamp in UTC. * $S = The distinguished name of the cert's subject." [cert :- Certificate - inventory-file :- schema/Str] - (let [serial-number (->> cert - (.getSerialNumber) - (format-serial-number) - (str "0x")) + {:keys [cert-inventory] :as settings} :- CaSettings] + (let [serial-number (.getSerialNumber cert) + formatted-serial-number (->> serial-number + (format-serial-number) + (str "0x")) not-before (-> cert (.getNotBefore) (format-date-time)) @@ -594,8 +769,119 @@ (.getNotAfter) (format-date-time)) subject (utils/get-subject-from-x509-certificate cert) - entry (str serial-number " " not-before " " not-after " /" subject "\n")] - (spit inventory-file entry :append true))) + cert-name (utils/x500-name->CN subject) + entry (str formatted-serial-number " " not-before " " not-after " /" subject "\n")] + (log/debug (i18n/trs "Append \"{1}\" to inventory file {0}" cert-inventory entry)) + (ks-file/atomic-write cert-inventory (partial stream-content-to-file cert-inventory entry)) + (maybe-write-to-infra-serial! serial-number cert-name settings))) + +(schema/defn ^:always-validate + write-cert-to-inventory! + "Same behavior as `write-cert-to-inventory-unlocked! but acquires the inventory lock prior to doing the work. + Writes an entry into Puppet's inventory file for a given certificate. + The location of this file is defined by Puppet's 'cert_inventory' setting. + The inventory is a text file where each line represents a certificate in the + following format: + $SN $NB $NA /$S + where: + * $SN = The serial number of the cert. The serial number is formatted as a + hexadecimal number, with a leading 0x, and zero-padded up to four + digits, eg. 0x002f. + * $NB = The 'not before' field of the cert, as a date/timestamp in UTC. + * $NA = The 'not after' field of the cert, as a date/timestamp in UTC. + * $S = The distinguished name of the cert's subject." + [cert :- Certificate + {:keys [inventory-lock inventory-lock-timeout-seconds] :as settings} :- CaSettings] + (common/with-safe-write-lock inventory-lock inventory-lock-descriptor inventory-lock-timeout-seconds + (write-cert-to-inventory-unlocked! cert settings))) + +(schema/defn is-subject-in-inventory-row? :- schema/Bool + [cn-subject :- utils/ValidX500Name + [_serial _not-before _not-after row-subject] :- [schema/Str]] + ;; row subject always starts with a slash, so drop it. + (if (some? row-subject) + (= (subs row-subject 1) cn-subject) + false)) + +(schema/defn is-not-expired? :- schema/Bool + [now :- DateTime + [_serial _not-before not-after _row-subject] :- [schema/Str]] + (if-let [not-after-date (parse-date-time not-after)] + (time/before? now not-after-date) + ;; lack of an end date means we can't tell if it is expired or not, so assume it isn't. + false)) + +(schema/defn is-expired? :- schema/Bool + [now :- DateTime + [_serial _not-before not-after _row-subject] :- [schema/Str]] + (if-let [not-after-date (parse-date-time not-after)] + (time/after? now not-after-date) + ;; lack of an end date means we can't tell if it is expired or not, so assume it isn't. + false)) + +(defn extract-inventory-row-contents + [row] + (str/split row #" ")) + +(schema/defn in-cert-inventory-file? :- schema/Bool + [{:keys [cert-inventory inventory-lock inventory-lock-timeout-seconds]} :- CaSettings + certname :- schema/Str] + (common/with-safe-read-lock inventory-lock inventory-lock-descriptor inventory-lock-timeout-seconds + (log/trace (i18n/trs "Looking for \"{0}\" in inventory file {1}" certname cert-inventory)) + (if (fs/exists? cert-inventory) + (with-open [inventory-reader (io/reader cert-inventory)] + (let [inventory-rows (map extract-inventory-row-contents (line-seq inventory-reader)) + cn-subject (utils/cn certname)] + (some? (some (partial is-subject-in-inventory-row? cn-subject) inventory-rows)))) + (do + (log/debug "Unable to find inventory file {0}" cert-inventory) + false)))) + +(schema/defn base-16-str->biginteger :- BigInteger + "Given a base-16 string with a leading 0x, return the result as a BigInteger" + [serial :- schema/Str] + (BigInteger. ^String (subs serial 2) 16)) + +(schema/defn expired-inventory-serials :- [BigInteger] + [{:keys [cert-inventory inventory-lock inventory-lock-timeout-seconds]} :- CaSettings] + (common/with-safe-read-lock inventory-lock inventory-lock-descriptor inventory-lock-timeout-seconds + (log/trace (i18n/trs "Extracting expired serials from inventory file {0}" cert-inventory)) + (if (fs/exists? cert-inventory) + (with-open [inventory-reader (io/reader cert-inventory)] + (let [now (time/now)] + (doall + (->> + (line-seq inventory-reader) + (map extract-inventory-row-contents ) + (filter (partial is-expired? now)) + (map first) + ;; assume serials are base 16 strings + (map base-16-str->biginteger))))) + (do + (log/debug "Unable to find inventory file {0}" cert-inventory) + [])))) + +(schema/defn find-matching-valid-serial-numbers :- [BigInteger] + [{:keys [cert-inventory inventory-lock inventory-lock-timeout-seconds]} :- CaSettings + certname :- schema/Str] + (common/with-safe-read-lock inventory-lock inventory-lock-descriptor inventory-lock-timeout-seconds + (log/trace (i18n/trs "Looking for serial numbers for \"{0}\" in inventory file {1}" certname cert-inventory)) + (if (fs/exists? cert-inventory) + (with-open [inventory-reader (io/reader cert-inventory)] + (let [inventory-rows (map extract-inventory-row-contents (line-seq inventory-reader)) + cn-subject (utils/cn certname) + now (time/now)] + (doall + (->> inventory-rows + (filter (partial is-subject-in-inventory-row? cn-subject)) + (filter (partial is-not-expired? now)) + ;; serial number is first entry in the array + (map first) + ;; assume serials are base 16 strings, drop the `0x` as BigInteger won't deal with them. + (map base-16-str->biginteger))))) + (do + (log/debug (i18n/trs "Unable to find inventory file {0}" cert-inventory)) + [])))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Initialization @@ -658,41 +944,49 @@ netscape-comment-value) (utils/create-ca-extensions issuer-public-key ca-public-key)))) -(schema/defn read-infra-nodes - "Returns a list of infra nodes or infra node serials from the specified file organized as one item per line." - [infra-file :- schema/Str] - (line-seq (io/reader infra-file))) - -(defn- write-infra-serials-to-writer - [writer infra-nodes-path infra-node-serials-path signeddir] - (try - (let [infra-nodes (read-infra-nodes infra-nodes-path)] - (doseq [infra-node infra-nodes] - (try - (let [infra-serial (-> (path-to-cert signeddir infra-node) - (utils/pem->cert) - (utils/get-serial))] - (.write writer (str infra-serial)) - (.newLine writer)) - (catch java.io.FileNotFoundException ex - (log/warn - (i18n/trs - (str - "Failed to find/load certificate for Puppet Infrastructure Node:" - infra-node))))))) - (catch java.io.FileNotFoundException ex - (log/warn (i18n/trs (str infra-nodes-path " does not exist")))))) +(schema/defn extract-active-infra-serials :- [BigInteger] + "Read the infra nodes file to determine which nodes are infrastructure nodes. + For each node, check the inventory file for any serial numbers for that node, + Also check the filesystem for a signed cert for that node. Return a sorted unique + set of serial numbers for nodes in the infra file" + [{:keys [infra-nodes-path signeddir] :as settings} :- CaSettings] + (if (fs/exists? infra-nodes-path) + (with-open [infra-nodes-reader (io/reader infra-nodes-path)] + (let [infra-nodes (read-infra-nodes infra-nodes-reader)] + (loop [local-infra-nodes infra-nodes + result []] + (if-let [infra-node (first local-infra-nodes)] + (let [augmented-results (concat result (find-matching-valid-serial-numbers settings infra-node)) + cert-path (path-to-cert signeddir infra-node)] + (if (fs/exists? cert-path) + (let [serial (-> cert-path + utils/pem->cert + utils/get-serial)] + (recur (rest local-infra-nodes) + (conj augmented-results serial))) + (do + (log/warn (i18n/trs "Failed to find certificate for Puppet Infrastructure Node: {0}" infra-node)) + (recur (rest local-infra-nodes) + augmented-results)))) + ;; ensure they are unique and ordered so the end result is stable + (sort (set result)))))) + [])) + +(schema/defn write-infra-serials-to-writer + [writer :- BufferedWriter + settings :- CaSettings] + (let [infra-serials (extract-active-infra-serials settings)] + (doseq [infra-serial infra-serials] + (.write writer (str infra-serial)) + (.newLine writer)))) -(schema/defn generate-infra-serials +(schema/defn generate-infra-serials! "Given a list of infra nodes it will create a file containing serial numbers of their certificates (listed on separate lines). It is expected have at least one entry (MoM)" - [{:keys [infra-nodes-path infra-node-serials-path signeddir]} :- CaSettings] + [{:keys [infra-node-serials-path] :as settings} :- CaSettings] (ks-file/atomic-write infra-node-serials-path - #(write-infra-serials-to-writer % - infra-nodes-path - infra-node-serials-path - signeddir) + #(write-infra-serials-to-writer % settings) public-key-perms)) (defn symlink-cadir @@ -726,15 +1020,15 @@ (create-parent-directories! (vals (settings->cadir-paths ca-settings))) (-> ca-settings :csrdir fs/file ks/mkdirs!) (-> ca-settings :signeddir fs/file ks/mkdirs!) - (initialize-serial-file! (:serial ca-settings)) + (initialize-serial-file! ca-settings) (-> ca-settings :infra-nodes-path fs/file fs/create) - (generate-infra-serials ca-settings) + (generate-infra-serials! ca-settings) (let [keypair (utils/generate-key-pair (:keylength ca-settings)) public-key (utils/get-public-key keypair) private-key (utils/get-private-key keypair) x500-name (utils/cn (:ca-name ca-settings)) validity (cert-validity-dates (:ca-ttl ca-settings)) - serial (next-serial-number! (:serial ca-settings)) + serial (next-serial-number! ca-settings) ;; Since this is a self-signed cert, the issuer key and the ;; key for this cert are the same ca-exts (create-ca-extensions public-key @@ -754,7 +1048,7 @@ infra-crl (utils/generate-crl (.getIssuerX500Principal cacert) private-key public-key)] - (write-cert-to-inventory! cacert (:cert-inventory ca-settings)) + (write-cert-to-inventory! cacert ca-settings) (write-public-key public-key (:capub ca-settings)) (write-private-key private-key (:cakey ca-settings)) (write-cert cacert (:cacert ca-settings)) @@ -788,6 +1082,11 @@ (utils/subject-alt-names {:dns-name (conj default-alt-names host-name)} false) (utils/subject-alt-names (update alt-names-list :dns-name conj host-name) false)))) + +(def pattern-match-dot #"\.") +(def pattern-starts-with-alphanumeric-or-underscore #"^[\p{Alnum}_].*") +(def pattern-matches-alphanumeric-with-symbols-string #"^[\p{Alnum}\-_]*[\p{Alnum}_]$") + (schema/defn validate-subject! "Validate the CSR or certificate's subject name. The subject name must: * match the hostname specified in the HTTP request (the `subject` parameter) @@ -796,12 +1095,17 @@ * not contain the wildcard character (*)" [hostname :- schema/Str subject :- schema/Str] + (log/debug (i18n/trs "Checking \"{0}\" for validity" subject)) + (when-not (= hostname subject) + ;; see https://github.com/puppetlabs/clj-i18n/blob/main/README.md#single-quotes-in-messages for reasoning with double quote + (log/info (i18n/tru "Rejecting subject \"{0}\" because it doesn''t match hostname \"{1}\"" subject hostname)) (sling/throw+ {:kind :hostname-mismatch :msg (i18n/tru "Instance name \"{0}\" does not match requested key \"{1}\"" subject hostname)})) (when (contains-uppercase? hostname) + (log/info (i18n/tru "Rejecting subject \"{0}\" because all characters must be lowercase" subject)) (sling/throw+ {:kind :invalid-subject-name :msg (i18n/tru "Certificate names must be lower case.")})) @@ -810,11 +1114,25 @@ (sling/throw+ {:kind :invalid-subject-name :msg (i18n/tru "Subject contains a wildcard, which is not allowed: {0}" subject)})) - - (when-not (re-matches #"^([a-z0-9](?:(?:[a-z0-9\-_]*|(?ssldir-paths settings))) - (set-file-perms (:privatekeydir settings) private-key-dir-perms) + (ks-file/set-perms (:privatekeydir settings) private-key-dir-perms) (-> settings :certdir fs/file ks/mkdirs!) (-> settings :requestdir fs/file ks/mkdirs!) (let [ca-cert (utils/pem->ca-cert (:cacert ca-settings) (:cakey ca-settings)) ca-private-key (utils/pem->private-key (:cakey ca-settings)) - next-serial (next-serial-number! (:serial ca-settings)) + next-serial (next-serial-number! ca-settings) public-key (generate-master-ssl-keys! settings) extensions (create-master-extensions certname public-key @@ -974,7 +1291,7 @@ x500-name public-key extensions)] - (write-cert-to-inventory! hostcert (:cert-inventory ca-settings)) + (write-cert-to-inventory! hostcert ca-settings) (write-cert hostcert (:hostcert settings)) (write-cert hostcert (path-to-cert (:signeddir ca-settings) certname)))) @@ -992,9 +1309,9 @@ (fs/exists? hostcert) (throw (IllegalStateException. - (i18n/trs "Found master cert ''{0}'' but master private key ''{1}'' is missing" - hostcert - hostprivkey))) + ^String (i18n/trs "Found master cert ''{0}'' but master private key ''{1}'' is missing" + hostcert + hostprivkey))) :else (generate-master-ssl-files! settings certname ca-settings))) @@ -1009,10 +1326,10 @@ (do (ks/mkdirs! (fs/parent localcacert)) (fs/copy cacert localcacert)) - (if-not (fs/exists? localcacert) + (when-not (fs/exists? localcacert) (throw (IllegalStateException. - (i18n/trs ":localcacert ({0}) could not be found and no file at :cacert ({1}) to copy it from" - localcacert cacert)))))) + ^String (i18n/trs ":localcacert ({0}) could not be found and no file at :cacert ({1}) to copy it from" + localcacert cacert)))))) (schema/defn ^:always-validate retrieve-ca-crl! "Ensure a local copy of the CA CRL, if one exists, is available on disk. @@ -1127,9 +1444,9 @@ ruby-load-path) (map ks/absolute-path) (str/join (System/getProperty "path.separator"))) - gempath (->> (if-let [gems (get env "GEM_PATH")] - (str gems (System/getProperty "path.separator") gem-path) - gem-path)) + gempath (if-let [gems (get env "GEM_PATH")] + (str gems (System/getProperty "path.separator") gem-path) + gem-path) results (shell-utils/execute-command executable {:args [subject] @@ -1154,15 +1471,22 @@ config->ca-settings :- CaSettings "Given the configuration map from the Puppet Server config service return a map with of all the CA settings." - [{:keys [puppetserver jruby-puppet certificate-authority]}] - (-> (select-keys puppetserver (keys CaSettings)) - (merge (select-keys certificate-authority (keys CaSettings))) - (initialize-ca-config) - (assoc :ruby-load-path (:ruby-load-path jruby-puppet)) - (assoc :gem-path (str/join (System/getProperty "path.separator") - (:gem-path jruby-puppet))) - (assoc :access-control (select-keys certificate-authority - [:certificate-status])))) + [{:keys [puppetserver jruby-puppet certificate-authority authorization]}] + (let [merged (-> (select-keys puppetserver (keys CaSettings)) + (merge (select-keys certificate-authority (keys CaSettings))) + (initialize-ca-config))] + (assoc merged :ruby-load-path (:ruby-load-path jruby-puppet) + :allow-auto-renewal (:allow-auto-renewal merged) + :auto-renewal-cert-ttl (duration-str->sec (:auto-renewal-cert-ttl merged)) + :ca-ttl (get-ca-ttl puppetserver certificate-authority) + :allow-header-cert-info (get authorization :allow-header-cert-info false) + :gem-path (str/join (System/getProperty "path.separator") + (:gem-path jruby-puppet)) + :access-control (select-keys certificate-authority + [:certificate-status]) + :serial-lock (new ReentrantReadWriteLock) + :crl-lock (new ReentrantReadWriteLock) + :inventory-lock (new ReentrantReadWriteLock)))) (schema/defn ^:always-validate config->master-settings :- MasterSettings @@ -1171,10 +1495,10 @@ [{:keys [puppetserver]}] (select-keys puppetserver (keys MasterSettings))) -(schema/defn ^:always-validate - get-certificate :- (schema/maybe schema/Str) - "Given a subject name and paths to the certificate directory and the CA - certificate, return the subject's certificate as a string, or nil if not found. +(schema/defn ^:always-validate get-certificate-path :- (schema/maybe schema/Str) + "Given a subject name and paths to the CA certificate and path + to the certificate directory return the path to the subject's + certificate as a string, or nil if not found. If the subject is 'ca', then use the `cacert` path instead." [subject :- schema/Str cacert :- schema/Str @@ -1182,8 +1506,19 @@ (let [cert-path (if (= "ca" subject) cacert (path-to-cert signeddir subject))] - (if (fs/exists? cert-path) - (slurp cert-path)))) + (when (fs/exists? cert-path) + cert-path))) + +(schema/defn ^:always-validate + get-certificate :- (schema/maybe schema/Str) + "Given a subject name and path to the certificate directory and the CA + certificate, return the subject's certificate as a string, or nil if not found. + If the subject is 'ca', then use the `cacert` path instead." + [subject :- schema/Str + cacert :- schema/Str + signeddir :- schema/Str] + (when-let [cert-path (get-certificate-path subject cacert signeddir)] + (slurp cert-path))) (schema/defn ^:always-validate get-certificate-request :- (schema/maybe schema/Str) @@ -1192,18 +1527,30 @@ [subject :- schema/Str csrdir :- schema/Str] (let [cert-request-path (path-to-cert-request csrdir subject)] - (if (fs/exists? cert-request-path) + (when (fs/exists? cert-request-path) (slurp cert-request-path)))) (schema/defn ^:always-validate + get-paths-to-all-certificate-requests :- [Path] + "Given a csr directory, return Path entries to all the files that could be CSRs" + [csrdir :- schema/Str] + (let [csr-dir-as-path (Paths/get csrdir (into-array String []))] + (if (Files/isDirectory csr-dir-as-path ks-file/nofollow-links) + (with-open [dir-stream (Files/newDirectoryStream csr-dir-as-path "*.pem")] + (doall (iterator-seq (.iterator dir-stream)))) + (do + (log/error (i18n/trs "Attempting to use {0} as CSR directory, but it is not a directory." csrdir)) + [])))) + +(schema/defn ^:always-validate autosign-csr? :- schema/Bool "Return true if the CSR should be automatically signed given Puppet's autosign setting, and false otherwise." - ([autosign :- (schema/either schema/Str schema/Bool) + ([autosign :- AutoSignInput subject :- schema/Str csr-stream :- InputStream] (autosign-csr? autosign subject csr-stream [] "")) - ([autosign :- (schema/either schema/Str schema/Bool) + ([autosign :- AutoSignInput subject :- schema/Str csr-stream :- InputStream ruby-load-path :- [schema/Str] @@ -1212,10 +1559,11 @@ autosign (if (fs/exists? autosign) (if (fs/executable? autosign) - (let [command-result (execute-autosign-command! autosign subject csr-stream ruby-load-path gem-path)] - (-> command-result - :exit-code - zero?)) + (let [command-result (execute-autosign-command! autosign subject csr-stream ruby-load-path gem-path) + succeed? (zero? (:exit-code command-result))] + (when-not succeed? + (log/debug (i18n/trs "Autosign executable failed. Result: {0} " (pr-str command-result)))) + succeed?) (whitelist-matches? autosign subject)) false)))) @@ -1229,7 +1577,7 @@ csr-ext-list (utils/get-extensions csr) base-ext-list [(utils/netscape-comment netscape-comment-value) - (utils/authority-key-identifier + (utils/authority-key-identifier-options cacert) (utils/basic-constraints-for-non-ca true) (utils/ext-key-usages @@ -1243,20 +1591,95 @@ combined-list (vec (concat base-ext-list csr-ext-list))] (ensure-ext-list-has-cn-san subject combined-list))) +(defn report-cert-event + "Log message and report to the activity service if available about cert activties, ie signing and revoking." + [report-activity message subject certnames ip-address activity-type] + (let [commit {:service {:id "puppet-ca"} + :subject {:id subject + :name subject + :type "users"} + :objects (mapv (fn [cert] {:type "node" :id cert :name cert}) certnames) + :events [{:type (str activity-type "-certificate") + :what "node" + :description (str "certificate_successfully_" activity-type) + :message message}] + :ip_address ip-address}] + (log/info message) + (report-activity {:commit commit}))) + +(defn generate-cert-message-from-request + "Extract params from request and create successful cert signing message. + Returns message, subject, certname and ip address" + [request subjects activity-type] + (let [auth-name (get-in request [:authorization :name]) + rbac-user (get-in request [:rbac-subject :login]) + ip-address (:remote-addr request) + signee (first (remove clojure.string/blank? [rbac-user auth-name "CA"]))] + + [(i18n/trsn "Entity {1} {2} 1 certificate: {3}." + "Entity {1} {2} {0} certificates: {3}." + (count subjects) signee activity-type (str/join ", " subjects)) + signee + subjects + ip-address])) + +(defn create-report-activity-fn + [report-activity request] + (fn [subjects activity-type] + (let [[msg signee certnames ip] (generate-cert-message-from-request request subjects activity-type)] + (report-cert-event report-activity msg signee certnames ip activity-type)))) + +(schema/defn supports-auto-renewal? :- schema/Bool + "Given a csr, determine if the requester is capable of supporting auto-renewal by looking for a specific attribute" + [csr] + (if-let [auto-renew-attribute (first (filter #(= pp_auth_auto_renew-attribute (:oid %)) (get-csr-attributes csr)))] + (do + (log/debug (i18n/trs "Found auto-renew-attribute {0}" (first (:values auto-renew-attribute)))) + ;; the values is a sequence of results, assume the first one is correct. + (= "true" (first (:values auto-renew-attribute)))) + false)) + +(schema/defn ^:always-validate delete-certificate-request! :- OutcomeInfo + "Delete pending certificate requests for subject" + [{:keys [csrdir]} :- CaSettings + subject :- schema/Str] + (let [csr-path (path-to-cert-request csrdir subject)] + + (if (fs/exists? csr-path) + (if (fs/delete csr-path) + (let [msg (i18n/trs "Deleted certificate request for {0} at {1}" subject csr-path)] + (log/debug msg) + {:outcome :success + :message msg}) + (let [msg (i18n/trs "Path {0} exists but could not be deleted" csr-path)] + (log/error msg) + {:outcome :error + :message msg})) + (let [msg (i18n/trs "No certificate request for {0} at expected path {1}" + subject csr-path)] + (log/warn msg) + {:outcome :not-found + :message msg})))) + (schema/defn ^:always-validate autosign-certificate-request! "Given a subject name, their certificate request, and the CA settings from Puppet, auto-sign the request and write the certificate to disk." [subject :- schema/Str csr :- CertificateRequest - {:keys [cacert cakey signeddir ca-ttl serial cert-inventory]} :- CaSettings] - (let [validity (cert-validity-dates ca-ttl) + {:keys [cacert cakey signeddir ca-ttl allow-auto-renewal auto-renewal-cert-ttl] :as ca-settings} :- CaSettings + report-activity] + (let [renewal-ttl (if (and allow-auto-renewal (supports-auto-renewal? csr)) + auto-renewal-cert-ttl + ca-ttl) + _ (log/debug (i18n/trs "Calculating validity dates for {0} from ttl of {1} " subject renewal-ttl)) + validity (cert-validity-dates renewal-ttl) ;; if part of a CA bundle, the intermediate CA will be first in the chain cacert (utils/pem->ca-cert cacert cakey) signed-cert (utils/sign-certificate (utils/get-subject-from-x509-certificate cacert) (utils/pem->private-key cakey) - (next-serial-number! serial) + (next-serial-number! ca-settings) (:not-before validity) (:not-after validity) (utils/cn subject) @@ -1264,9 +1687,10 @@ (create-agent-extensions csr cacert))] - (log/info (i18n/trs "Signed certificate request for {0}" subject)) - (write-cert-to-inventory! signed-cert cert-inventory) - (write-cert signed-cert (path-to-cert signeddir subject)))) + (write-cert-to-inventory! signed-cert ca-settings) + (write-cert signed-cert (path-to-cert signeddir subject)) + (delete-certificate-request! ca-settings subject) + (report-activity [subject] "signed"))) (schema/defn ^:always-validate save-certificate-request! @@ -1278,23 +1702,29 @@ (log/debug (i18n/trs "Saving CSR to ''{0}''" csr-path)) (write-csr csr csr-path))) +(schema/defn is-revoked? :- schema/Bool + [cert :- X509Certificate + {:keys [cacert cacrl crl-lock crl-lock-timeout-seconds cakey]} :- CaSettings] + (utils/revoked? + (common/with-safe-read-lock crl-lock crl-lock-descriptor crl-lock-timeout-seconds + (utils/pem->ca-crl cacrl (utils/pem->ca-cert cacert cakey))) + cert)) + (schema/defn validate-duplicate-cert-policy! - "Throw a slingshot exception if allow-duplicate-certs is false + "Throw a slingshot exception if allow-duplicate-certs is false, and we already have a certificate or CSR for the subject. The exception map will look like: {:kind :duplicate-cert :msg }" [csr :- CertificateRequest - {:keys [allow-duplicate-certs cacert cacrl cakey csrdir signeddir]} :- CaSettings] + {:keys [allow-duplicate-certs csrdir signeddir] :as settings} :- CaSettings] (let [subject (get-csr-subject csr) cert (path-to-cert signeddir subject) existing-cert? (fs/exists? cert) existing-csr? (fs/exists? (path-to-cert-request csrdir subject))] (when (or existing-cert? existing-csr?) (let [status (if existing-cert? - (if (utils/revoked? - (utils/pem->ca-crl cacrl (utils/pem->ca-cert cacert cakey)) - (utils/pem->cert cert)) + (if (is-revoked? (utils/pem->cert cert) settings) "revoked" "signed") "requested")] @@ -1322,7 +1752,7 @@ (let [extensions (utils/get-extensions csr)] (doseq [extension extensions] (when (utils/subtree-of? ppAuthCertExt (:oid extension)) - (if (false? allow-authorization-extensions) + (when (false? allow-authorization-extensions) (sling/throw+ {:kind :disallowed-extension :msg (format "%s %s %s" @@ -1339,7 +1769,7 @@ [csr :- CertificateRequest allow-subject-alt-names :- schema/Bool] (when-let [subject-alt-names (not-empty (subject-alt-names csr))] - (if (false? allow-subject-alt-names) + (when (false? allow-subject-alt-names) (let [subject (get-csr-subject csr) cn-alt-name (str "DNS:" subject)] (if (and (= 1 (count subject-alt-names)) @@ -1355,13 +1785,15 @@ (i18n/tru "To allow subject alternative names, set allow-subject-alt-names to true in your ca.conf file.") (i18n/tru "Then restart the puppetserver and try signing this certificate again."))}))))))) + (schema/defn ^:always-validate process-csr-submission! "Given a CSR for a subject (typically from the HTTP endpoint), perform policy checks and sign or save the CSR (based on autosign). Throws a slingshot exception if the CSR is invalid." [subject :- schema/Str certificate-request :- InputStream - {:keys [autosign csrdir ruby-load-path gem-path allow-subject-alt-names allow-authorization-extensions] :as settings} :- CaSettings] + {:keys [autosign csrdir ruby-load-path gem-path allow-subject-alt-names allow-authorization-extensions] :as settings} :- CaSettings + report-activity] (with-open [byte-stream (-> certificate-request input-stream->byte-array ByteArrayInputStream.)] @@ -1375,44 +1807,33 @@ (ensure-no-authorization-extensions! csr allow-authorization-extensions) (validate-extensions! (utils/get-extensions csr)) (validate-csr-signature! csr) - (autosign-certificate-request! subject csr settings) - (fs/delete (path-to-cert-request csrdir subject)))))) - -(schema/defn ^:always-validate delete-certificate-request! :- OutcomeInfo - "Delete pending certificate requests for subject" - [{:keys [csrdir]} :- CaSettings - subject :- schema/Str] - (let [csr-path (path-to-cert-request csrdir subject)] - (if (fs/exists? csr-path) - (if (fs/delete csr-path) - (let [msg (i18n/trs "Deleted certificate request for {0}" subject)] - (log/debug msg) - {:outcome :success - :message msg}) - (let [msg (i18n/trs "Path {0} exists but could not be deleted" csr-path)] - (log/error msg) - {:outcome :error - :message msg})) - (let [msg (i18n/trs "No certificate request for {0} at expected path {1}" - subject csr-path)] - (log/warn msg) - {:outcome :not-found - :message msg})))) + (autosign-certificate-request! subject csr settings report-activity))))) (schema/defn ^:always-validate get-certificate-revocation-list :- schema/Str "Given the value of the 'cacrl' setting from Puppet, return the CRL from the .pem file on disk." - [cacrl :- schema/Str] - (slurp cacrl)) + [cacrl :- schema/Str + lock :- ReentrantReadWriteLock + lock-descriptor :- schema/Str + lock-timeout :- PosInt] + (common/with-safe-read-lock lock lock-descriptor lock-timeout + (slurp cacrl))) (schema/defn ^:always-validate - get-crl-last-modified :- DateTime - "Given the value of the 'cacrl' setting from Puppet, return - a Joda DateTime instance of when the CRL file was last modified." - [cacrl :- schema/Str] - (let [last-modified-milliseconds (.lastModified (io/file cacrl))] - (time-coerce/from-long last-modified-milliseconds))) + get-file-last-modified :- DateTime + "Given a path to a file, return a Joda DateTime instance of when the file was last modified. + an optional lock, description, and timeout may be passed to serialize access to files." + ([path :- schema/Str] + (let [last-modified-milliseconds (.lastModified (io/file path))] + (time-coerce/from-long last-modified-milliseconds))) + ([path :- schema/Str + lock :- ReentrantReadWriteLock + lock-descriptor :- schema/Str + lock-timeout :- PosInt] + (common/with-safe-read-lock lock lock-descriptor lock-timeout + (let [last-modified-milliseconds (.lastModified (io/file path))] + (time-coerce/from-long last-modified-milliseconds))))) (schema/defn ^:always-validate reject-delta-crl [crl :- CertificateRevocationList] @@ -1482,7 +1903,8 @@ (schema/defn ^:always-validate update-crls "Given a collection of CRLs, update the CRL chain and confirm that - all CRLs are currently valid." + all CRLs are currently valid. + NOTE: assumes appropriate locking is in place" [incoming-crls :- [X509CRL] crl-path :- schema/Str cert-chain-path :- schema/Str] @@ -1510,6 +1932,17 @@ (write-crls new-ext-crl-chain crl-path) (log/info (i18n/trs "Successfully updated CRL at {0}" crl-path)))) +(schema/defn update-crls! + "Apply write locking to the crls, and update the crls as appropriate." + [incoming-crls :- [X509CRL] + crl-path :- schema/Str + cacert :- schema/Str + {:keys [crl-lock crl-lock-timeout-seconds enable-infra-crl infra-crl-path]} :- CaSettings] + (common/with-safe-write-lock crl-lock crl-lock-descriptor crl-lock-timeout-seconds + (update-crls incoming-crls crl-path cacert) + (when enable-infra-crl + (update-crls incoming-crls infra-crl-path cacert)))) + (schema/defn ensure-directories-exist! "Create any directories used by the CA if they don't already exist." [settings :- CaSettings] @@ -1523,9 +1956,9 @@ does not, then correct them." [settings :- CaSettings] (let [ca-p-key (:cakey settings) - cur-perms (get-file-perms ca-p-key)] + cur-perms (ks-file/get-perms ca-p-key)] (when-not (= private-key-perms cur-perms) - (set-file-perms ca-p-key private-key-perms) + (ks-file/set-perms ca-p-key private-key-perms) (log/warn (format "%s %s" (i18n/trs "The private CA key at ''{0}'' was found to have the wrong permissions set as ''{1}''." ca-p-key cur-perms) @@ -1547,7 +1980,7 @@ (do (log/info (i18n/trs "CA already initialized for SSL")) (when (:enable-infra-crl settings) - (generate-infra-serials settings)) + (generate-infra-serials! settings)) (when (:manage-internal-file-permissions settings) (ensure-ca-file-perms! settings))) (let [{found true @@ -1561,7 +1994,7 @@ (schema/defn certificate-state :- CertificateState "Determine the state a certificate is in." - [cert-or-csr :- (schema/either Certificate CertificateRequest) + [cert-or-csr :- CertificateOrCSR crl :- CertificateRevocationList] (if (utils/certificate-request? cert-or-csr) "requested" @@ -1572,7 +2005,7 @@ (schema/defn fingerprint :- schema/Str "Calculate the hash of the certificate or CSR using the given algorithm, which must be one of SHA-1, SHA-256, or SHA-512." - [cert-or-csr :- (schema/either Certificate CertificateRequest) + [cert-or-csr :- CertificateOrCSR algorithm :- schema/Str] (->> (utils/fingerprint cert-or-csr algorithm) (partition 2) @@ -1596,7 +2029,7 @@ [crl :- CertificateRevocationList is-cert? :- schema/Bool subject :- schema/Str - cert-or-csr :- (schema/either Certificate CertificateRequest)] + cert-or-csr :- CertificateOrCSR] (let [default-fingerprint (fingerprint cert-or-csr "SHA-256")] (merge {:name subject @@ -1610,7 +2043,7 @@ :SHA512 (fingerprint cert-or-csr "SHA-512") :default default-fingerprint}} ;; Only certificates have expiry dates - (if is-cert? + (when is-cert? (get-certificate-details cert-or-csr))))) (schema/defn ^:always-validate get-cert-or-csr-status :- CertificateStatusResult @@ -1660,16 +2093,15 @@ (schema/defn sign-existing-csr! "Sign the subject's certificate request." [{:keys [csrdir] :as settings} :- CaSettings - subject :- schema/Str] + subject :- schema/Str + report-activity] (let [csr-path (path-to-cert-request csrdir subject)] - (autosign-certificate-request! subject (utils/pem->csr csr-path) settings) - (fs/delete csr-path) - (log/debug (i18n/trs "Removed certificate request for {0} at ''{1}''" subject csr-path)))) + (autosign-certificate-request! subject (utils/pem->csr csr-path) settings report-activity))) -(schema/defn filter-already-revoked-serials :- [schema/Int] +(schema/defn filter-already-revoked-serials :- [BigInteger] "Given a list of serials and Puppet's CA CRL, returns vector of serials with any already-revoked serials removed." - [serials :- [schema/Int] + [serials :- [BigInteger] crl :- X509CRL] (let [crl-revoked-list (.getRevokedCertificates crl) existed-serials (set (map #(.getSerialNumber %) crl-revoked-list)) @@ -1679,58 +2111,86 @@ (log/debug (i18n/trs "Certificate with serial {0} is already revoked." serial)))) (vec (set/difference (set serials) existed-serials)))) +(schema/defn get-cert-serial :- BigInteger + ;; will throw if the file doesn't exist + [path-to-cert] + (log/trace (i18n/trs "Try to read serial from \"{0}\"" path-to-cert)) + (-> path-to-cert + (utils/pem->cert) + (utils/get-serial))) +(schema/defn safe-get-cert-serial :- [BigInteger] + ;; Check to see if the file exists and attempt to read the serial from the file if it does + [path-to-cert] + (if (fs/exists? path-to-cert) + [(get-cert-serial path-to-cert)] + [])) + +(schema/defn look-for-serial-numbers :- [BigInteger] + [settings :- CaSettings + certname :- schema/Str] + ;; first look in the inventory (it is cheaper than reading certs). If it isn't there, read the cert + (let [inventory-certs (find-matching-valid-serial-numbers settings certname) + path-to-cert (path-to-cert (:signeddir settings) certname)] + (if-not (empty? inventory-certs) + ;; cover the corner case of the serial number in the cert in the file, but not in the inventory file + (concat inventory-certs (safe-get-cert-serial path-to-cert)) + ;; this will throw if the file isn't found, indicating it isn't present + [(get-cert-serial path-to-cert)]))) + (schema/defn revoke-existing-certs! "Revoke the subjects' certificates. Note this does not destroy the certificates. The certificates will remain in the signed directory despite being revoked." - [{:keys [signeddir cacert cacrl cakey infra-crl-path - infra-node-serials-path enable-infra-crl]} :- CaSettings - subjects :- [schema/Str]] - (let [[our-full-crl & rest-of-full-chain] (utils/pem->crls cacrl) - serials (filter-already-revoked-serials (map #(-> (path-to-cert signeddir %) - (utils/pem->cert) - (utils/get-serial)) - subjects) - our-full-crl) - serial-count (count serials)] - (if (= 0 serial-count) - (log/info (i18n/trs "No revoke action needed. The certs are already in the CRL.")) - (let [new-full-crl (utils/revoke-multiple our-full-crl - (utils/pem->private-key cakey) - (.getPublicKey (utils/pem->ca-cert cacert cakey)) - serials) - new-full-chain (cons new-full-crl (vec rest-of-full-chain))] - (write-crls new-full-chain cacrl) - (log/info (i18n/trsn "Revoked 1 certificate: {1}" - "Revoked {0} certificates: {1}" - serial-count - (str/join ", " subjects))))) - - - ;; Publish infra-crl if an infra node is getting revoked. - (when (and enable-infra-crl (fs/exists? infra-node-serials-path)) - (let [infra-nodes (set (map biginteger (read-infra-nodes infra-node-serials-path))) - infra-revocations (vec (set/intersection infra-nodes (set serials)))] - (when (seq infra-revocations) - (let [[our-infra-crl & rest-of-infra-chain] (utils/pem->crls infra-crl-path) - new-infra-revocations (filter-already-revoked-serials infra-revocations our-infra-crl)] - (if (= 0 new-infra-revocations) - (log/info (i18n/trs "No revoke action needed. The infra certs are already in the infra CRL")) - (let [new-infra-crl (utils/revoke-multiple our-infra-crl - (utils/pem->private-key cakey) - (.getPublicKey (utils/pem->ca-cert cacert cakey)) - new-infra-revocations) - full-infra-chain (cons new-infra-crl (vec rest-of-infra-chain))] - (write-crls full-infra-chain infra-crl-path) - (log/info (i18n/trs "Infra node certificate(s) being revoked; publishing updated infra CRL")))))))))) + [{:keys [cacert cacrl cakey infra-crl-path + crl-lock crl-lock-timeout-seconds + infra-node-serials-path enable-infra-crl] :as settings} :- CaSettings + subjects :- [schema/Str] + report-activity] + ;; because we need the crl to be consistent for the serials, maintain a write lock on the crl + ;; as reentrant read-write locks do not allow upgrading from a read lock to a write lock + (common/with-safe-write-lock crl-lock crl-lock-descriptor crl-lock-timeout-seconds + (let [[our-full-crl & rest-of-full-chain] (utils/pem->crls cacrl) + serials (filter-already-revoked-serials (->> subjects + (map (partial look-for-serial-numbers settings)) + flatten) + our-full-crl) + serial-count (count serials)] + (if (zero? serial-count) + (log/info (i18n/trs "No revoke action needed. The certs are already in the CRL.")) + (let [new-full-crl (utils/revoke-multiple our-full-crl + (utils/pem->private-key cakey) + (.getPublicKey (utils/pem->ca-cert cacert cakey)) + serials) + new-full-chain (cons new-full-crl (vec rest-of-full-chain))] + (write-crls new-full-chain cacrl))) + + ;; Publish infra-crl if an infra node is getting revoked. + (when (and enable-infra-crl (fs/exists? infra-node-serials-path)) + (with-open [infra-nodes-serial-path-reader (io/reader infra-node-serials-path)] + (let [infra-nodes (set (map biginteger (read-infra-nodes infra-nodes-serial-path-reader))) + infra-revocations (vec (set/intersection infra-nodes (set serials)))] + (when (seq infra-revocations) + (let [[our-infra-crl & rest-of-infra-chain] (utils/pem->crls infra-crl-path) + new-infra-revocations (filter-already-revoked-serials infra-revocations our-infra-crl)] + (if (empty? new-infra-revocations) + (log/info (i18n/trs "No revoke action needed. The infra certs are already in the infra CRL")) + (let [new-infra-crl (utils/revoke-multiple our-infra-crl + (utils/pem->private-key cakey) + (.getPublicKey (utils/pem->ca-cert cacert cakey)) + new-infra-revocations) + full-infra-chain (cons new-infra-crl (vec rest-of-infra-chain))] + (write-crls full-infra-chain infra-crl-path) + (log/info (i18n/trs "Infra node certificate(s) being revoked; publishing updated infra CRL"))))))))) + (report-activity subjects "revoked")))) (schema/defn ^:always-validate set-certificate-status! "Sign or revoke the certificate for the given subject." [settings :- CaSettings subject :- schema/Str - desired-state :- DesiredCertificateState] + desired-state :- DesiredCertificateState + report-activity] (if (= :signed desired-state) - (sign-existing-csr! settings subject) - (revoke-existing-certs! settings [subject]))) + (sign-existing-csr! settings subject report-activity) + (revoke-existing-certs! settings [subject] report-activity))) (schema/defn ^:always-validate certificate-exists? :- schema/Bool "Do we have a certificate for the given subject?" @@ -1763,7 +2223,7 @@ certificate policy will not be checked. If the CSR is invalid, returns a user-facing message. Otherwise, returns nil." - [{:keys [csrdir allow-subject-alt-names allow-authorization-extensions] :as settings} :- CaSettings + [{:keys [csrdir allow-subject-alt-names allow-authorization-extensions] :as _settings} :- CaSettings subject :- schema/Str] (let [csr (utils/pem->csr (path-to-cert-request csrdir subject)) csr-subject (get-csr-subject csr) @@ -1803,7 +2263,7 @@ shortnames" [custom-oid-mapping-file :- schema/Str] (if (fs/file? custom-oid-mapping-file) - (let [oid-mappings (:oid_mapping (yaml/parse-string (slurp custom-oid-mapping-file)))] + (let [oid-mappings (:oid_mapping (common/parse-yaml (slurp custom-oid-mapping-file)))] (into {} (for [[oid names] oid-mappings] [(name oid) (keyword (:shortname names))]))) (log/debug (i18n/trs "No custom OID mapping configuration file found at {0}, custom OID mappings will not be loaded" custom-oid-mapping-file)))) @@ -1847,3 +2307,210 @@ (format-date-time)))) {} crl-chain))) + +(schema/defn cert-authority-id-match-ca-subject-id? :- schema/Bool + "Given a certificate, and the ca-cert, validate that the certificate was signed by the CA provided" + [incoming-cert :- X509Certificate + ca-cert :- X509Certificate] + (let [incoming-key-id (utils/get-extension-value incoming-cert utils/authority-key-identifier-oid) + ca-key-id (utils/get-extension-value ca-cert utils/subject-key-identifier-oid)] + (if incoming-key-id + ;; incoming are byte-arrays, convert to seq for simple comparisons + (= (seq (:key-identifier incoming-key-id)) (seq ca-key-id)) + false))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Public utilities + +(schema/defn replace-authority-identifier :- utils/SSLExtensionList + [extensions :- utils/SSLExtensionList ca-cert :- X509Certificate] + (conj (filter #(not= utils/authority-key-identifier-oid (:oid %)) extensions) + (utils/authority-key-identifier-options ca-cert))) + +(schema/defn replace-subject-identifier :- utils/SSLExtensionList + [extensions :- utils/SSLExtensionList subject-public-key :- PublicKey] + (conj (filter #(not= utils/subject-key-identifier-oid (:oid %)) extensions) + (utils/subject-key-identifier subject-public-key false))) + +(schema/defn update-extensions-for-new-signing :- utils/SSLExtensionList + [extensions :- utils/SSLExtensionList ca-cert :- X509Certificate subject-public-key :- PublicKey] + (replace-subject-identifier (replace-authority-identifier extensions ca-cert) subject-public-key)) + +(schema/defn renew-certificate! :- X509Certificate + "Given a certificate and CaSettings create a new signed certificate using the public key from the certificate. + It recreates all the extensions in the original certificate." + [certificate :- X509Certificate + {:keys [cacert cakey auto-renewal-cert-ttl signeddir] :as ca-settings} :- CaSettings + report-activity] + (let [validity (cert-validity-dates (or auto-renewal-cert-ttl default-auto-ttl-renewal-seconds)) + cacert (utils/pem->ca-cert cacert cakey) + cert-subject (utils/get-subject-from-x509-certificate certificate) + cert-name (utils/x500-name->CN cert-subject) + signed-cert (utils/sign-certificate + (utils/get-subject-from-x509-certificate cacert) + (utils/pem->private-key cakey) + (next-serial-number! ca-settings) + (:not-before validity) + (:not-after validity) + cert-subject + (.getPublicKey certificate) + (update-extensions-for-new-signing + (utils/get-extensions certificate) + cacert + (.getPublicKey certificate)))] + (write-cert-to-inventory! signed-cert ca-settings) + (write-cert signed-cert (path-to-cert signeddir cert-name)) + (log/info (i18n/trs "Renewed certificate for \"{0}\" with new expiration of \"{1}\"" + cert-name + (.format (new SimpleDateFormat "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") + (.getNotAfter signed-cert)))) + (report-activity [cert-subject] "renewed") + signed-cert)) + +(schema/defn crl-expires-in-n-days? + [crl-path {:keys [crl-lock crl-lock-timeout-seconds]} :- CaSettings + days :- schema/Int] + (common/with-safe-write-lock crl-lock crl-lock-descriptor crl-lock-timeout-seconds + (let [crl-object (first (utils/pem->crls crl-path)) + next-update (-> crl-object + .getNextUpdate + .toInstant + (.atZone (ZoneId/systemDefault)) + .toLocalDateTime)] + (.isBefore next-update (.plusDays (LocalDateTime/now) days))))) + +(schema/defn overwrite-existing-crl! + [crl :- X509CRL + rest-of-full-chain + capub :- schema/Str + cakey :- schema/Str + cacert :- X509Certificate + valid-serials :- [BigInteger] + crl-path :- schema/Str] + (let [^BigInteger crl-number (utils/get-crl-number crl) + public-key (utils/pem->public-key capub) + private-key (utils/pem->private-key cakey) + ;; because we are purging existing serials, we need a whole new CRL + ;; the "next update" and "crl-number" will get updated by the process that + ;; adds the serial numbers to the crl + new-full-crl (utils/generate-crl + ;; ensure the issuer is identical + (.getIssuerX500Principal crl) + private-key + public-key + (.getThisUpdate crl) + (.getNextUpdate crl) + crl-number + ;; original crl is generated with no extensions, continue the precedence + nil) + ;; create a new CRL with incremented "next update" and crl-number, and all the serials + new-crl-with-revoked (utils/revoke-multiple new-full-crl + (utils/pem->private-key cakey) + (.getPublicKey cacert) + valid-serials) + new-full-chain (cons new-crl-with-revoked (vec rest-of-full-chain))] + (write-crls new-full-chain crl-path))) +(schema/defn update-and-sign-crl! + "Given a path to a CRL, and the ca-settings, update the CRl with all known valid serials that have been revoked" + [path-to-crl {:keys [crl-lock crl-lock-timeout-seconds cacert cakey capub] :as settings} :- CaSettings] + ;; read in the existing crl, and extract the serial numbers that have expired so we can remove them from the CRL set. + (let [expired-serials (set (expired-inventory-serials settings))] + (common/with-safe-write-lock crl-lock crl-lock-descriptor crl-lock-timeout-seconds + (let [[our-full-crl & rest-of-full-chain] (utils/pem->crls path-to-crl) + crl-serials (set (map #(.getSerialNumber %) (.getRevokedCertificates our-full-crl))) + valid-crl-serials (set/difference crl-serials expired-serials) + cacert (utils/pem->ca-cert cacert cakey)] + (overwrite-existing-crl! our-full-crl rest-of-full-chain capub cakey cacert (vec valid-crl-serials) path-to-crl))))) + +(schema/defn maybe-update-crls-for-expiration + [{:keys [cacrl enable-infra-crl infra-crl-path] :as settings} :- CaSettings] + ;; check the age of the main crl + (when (crl-expires-in-n-days? cacrl settings crl-expiration-window-days) + (log/info (i18n/trs "CA CRL expiring within 30 days, updating.")) + (update-and-sign-crl! cacrl settings)) + (when (and enable-infra-crl (fs/exists? infra-crl-path)) + (when (crl-expires-in-n-days? infra-crl-path settings crl-expiration-window-days) + (log/info (i18n/trs "infra crl expiring within 30 days, updating.")) + (update-and-sign-crl! infra-crl-path settings)))) + +(schema/defn maybe-sign-one :- (schema/enum :signed :signing-errors) + [subject :- schema/Str + csr-path :- schema/Str + cacert :- Certificate + casubject :- schema/Str + ca-private-key :- PrivateKey + {:keys [signeddir ca-ttl allow-auto-renewal allow-subject-alt-names + allow-authorization-extensions auto-renewal-cert-ttl] :as ca-settings} :- CaSettings] + (try + (let [csr (utils/pem->csr csr-path) + renewal-ttl (if (and allow-auto-renewal (supports-auto-renewal? csr)) + auto-renewal-cert-ttl + ca-ttl) + _ (log/debug (i18n/trs "Calculating validity dates from ttl of {0} " renewal-ttl)) + validity (cert-validity-dates renewal-ttl)] + ;; these ensure/validate functions throw exceptions if the criteria isn't met + (ensure-subject-alt-names-allowed! csr allow-subject-alt-names) + (ensure-no-authorization-extensions! csr allow-authorization-extensions) + (validate-extensions! (utils/get-extensions csr)) + (validate-csr-signature! csr) + (let [signed-cert (utils/sign-certificate casubject + ca-private-key + (next-serial-number! ca-settings) + (:not-before validity) + (:not-after validity) + (utils/cn subject) + (utils/get-public-key csr) + (create-agent-extensions csr cacert))] + (write-cert-to-inventory-unlocked! signed-cert ca-settings) + (write-cert signed-cert (path-to-cert signeddir subject)) + (delete-certificate-request! ca-settings subject) + ;; success case, add the host to the set of signed results + :signed)) + (catch Throwable e + (log/debug e (i18n/trs "Failed in bulk signing for entry {0}" subject)) + ;; failure case, add the host to the set of not signed results + :signing-errors))) + +(schema/defn ^:always-validate + sign-multiple-certificate-signing-requests! :- {:signed [schema/Str] + :no-csr [schema/Str] + :signing-errors [schema/Str]} + [subjects :- [schema/Str] + {:keys [cacert cakey csrdir + inventory-lock inventory-lock-timeout-seconds + serial-lock serial-lock-timeout-seconds] :as ca-settings} :- CaSettings + report-activity] + (let [;; if part of a CA bundle, the intermediate CA will be first in the chain + cacert (utils/pem->ca-cert cacert cakey) + casubject (utils/get-subject-from-x509-certificate cacert) + ca-private-key (utils/pem->private-key cakey)] + (when-not (empty? subjects) + ;; since we are going to be manipulating the serial file and the inventory file for multiple entries, + ;; acquire the locks to prevent lock thrashing + (common/with-safe-write-lock serial-lock serial-lock-descriptor serial-lock-timeout-seconds + (common/with-safe-write-lock inventory-lock inventory-lock-descriptor inventory-lock-timeout-seconds + (let [results + ;; loop through the subjects, one at a time, and collect the results for success or failure. + (loop [s subjects + result {:signed [] + :no-csr [] + :signing-errors[]}] + (if-not (empty? s) + (let [subject (first s) + csr-path (path-to-cert-request csrdir subject)] + (if (fs/exists? csr-path) + (let [_ (log/trace (i18n/trs "File exists at {0}" csr-path)) + one-result (maybe-sign-one subject csr-path cacert casubject ca-private-key ca-settings)] + ;; one-result is either :signed or :signing-errors + (recur (rest s) + (update result one-result conj subject))) + (do + (log/trace (i18n/trs "File does not exist at {0}" csr-path)) + (recur (rest s) + (update result :no-csr conj subject))))) + result))] + ;; submit the signing activity as one entry for all the hosts. + (when-not (empty? (:signed results)) + (report-activity (:signed results) "signed")) + results)))))) \ No newline at end of file diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/cli/subcommand.clj puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/cli/subcommand.clj --- puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/cli/subcommand.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/cli/subcommand.clj 2024-01-15 23:29:55.000000000 +0000 @@ -44,7 +44,7 @@ (run-fn (load-tk-config config) extra-args)) (catch map? m (let [kind (:kind m)] - (if (keyword? kind) + (when (keyword? kind) (case (ks/without-ns kind) :cli-error (print-message-and-exit m 1) :cli-help (print-message-and-exit m 0) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/common.clj puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/common.clj --- puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/common.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/common.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,12 @@ (ns puppetlabs.puppetserver.common - (:require [schema.core :as schema] - [puppetlabs.i18n.core :as i18n])) + (:require [clojure.string :as str] + [clojure.tools.logging :as log] + [puppetlabs.i18n.core :as i18n] + [schema.core :as schema] + [slingshot.slingshot :as sling]) + (:import (java.util List Map Set) + (java.util.concurrent TimeUnit) + (org.yaml.snakeyaml Yaml))) (def Environment "Schema for environment names. Alphanumeric and _ only." @@ -21,3 +27,117 @@ (i18n/tru "Invalid code-id ''{0}''. Must contain only alpha-numerics and ''-'', ''_'', '';'', or '':''" code-id)) +(defmacro with-safe-read-lock + "Given a ReentrantReadWriteLock, acquire the read lock, and hold it for the length of the execution of the body. + If the lock can't be acquired, throw an exception to indicate a timeout. Log behaviors at trace level to aid with + supportability" + [read-write-lock descriptor timeout-in-seconds & body] + `(let [l# (.readLock ~read-write-lock) + descriptor# ~descriptor + timeout# ~timeout-in-seconds] + (log/trace (i18n/trs "Attempt to acquire read lock \"{0}\"" descriptor#)) + (if (.tryLock l# timeout# TimeUnit/SECONDS) + (try + (log/trace (i18n/trs "Acquired read lock \"{0}\"" descriptor#)) + (do + ~@body) + (finally + (.unlock l#) + (log/trace (i18n/trs "Released read lock \"{0}\"" descriptor#)))) + (do + (log/info (i18n/trs "Read Lock acquisition timed out \"{0}\"" descriptor#)) + (sling/throw+ + {:kind :lock-acquisition-timeout + :msg (i18n/tru "Failed to acquire read lock \"{0}\" within {1} seconds" descriptor# timeout#)}))))) + +(defmacro with-safe-write-lock + "Given a ReentrantReadWriteLock, acquire the write lock, and hold it for the length of the execution of the body. + If the lock can't be acquired, throw an exception to indicate a timeout. Log behaviors at trace level to aid with + supportability" + [read-write-lock descriptor timeout-in-seconds & body] + `(let [l# (.writeLock ~read-write-lock) + descriptor# ~descriptor + timeout# ~timeout-in-seconds] + (log/trace (i18n/trs "Attempt to acquire write lock \"{0}\"" descriptor#)) + (if (.tryLock l# timeout# TimeUnit/SECONDS) + (try + (log/trace (i18n/trs "Acquired write lock \"{0}\"" descriptor#)) + (do + ~@body) + (finally + (.unlock l#) + (log/trace (i18n/trs "Released write lock \"{0}\"" descriptor#)))) + (do + (log/info (i18n/trs "Write Lock acquisition timed out \"{0}\"" descriptor#)) + (sling/throw+ + {:kind :lock-acquisition-timeout + :msg (i18n/tru "Failed to acquire write lock \"{0}\" within {1} seconds" descriptor# timeout#)}))))) + +(defmacro with-safe-lock + "Given a ReentrantLock, acquire the lock, and hold it for the length of the execution of the body. + If the lock can't be acquired, throw an exception to indicate a timeout. Log behaviors at trace level to aid with + supportability" + [reentrant-lock descriptor timeout-in-seconds & body] + `(let [l# ~reentrant-lock + descriptor# ~descriptor + timeout# ~timeout-in-seconds] + (log/trace (i18n/trs "Attempt to acquire lock \"{0}\"" descriptor#)) + (if (.tryLock l# timeout# TimeUnit/SECONDS) + (try + (log/trace (i18n/trs "Acquired lock \"{0}\"" descriptor#)) + (do + ~@body) + (finally + (.unlock l#) + (log/trace (i18n/trs "Released lock \"{0}\"" descriptor#)))) + (do + (log/info (i18n/trs "Lock acquisition timed out \"{0}\"" descriptor#)) + (sling/throw+ + {:kind :lock-acquisition-timeout + :msg (i18n/tru "Failed to acquire lock \"{0}\" within {1} seconds" descriptor# timeout#)}))))) + +(defprotocol JavaMap->ClojureMap + (java->clj [o])) + +(extend-protocol JavaMap->ClojureMap + Map + (java->clj [o] (let [entries (.entrySet o)] + (reduce (fn [m [^String k v]] + (assoc m (keyword k) (java->clj v))) + {} entries))) + + List + (java->clj [o] (vec (map java->clj o))) + + Set + (java->clj [o] (set (map java->clj o))) + + Object + (java->clj [o] o) + + nil + (java->clj [_] nil)) + +(defn parse-yaml + [yaml-string] + ;; default in snakeyaml 2.0 is to not allow + ;; global tags, which is the source of exploits. + (let [yaml (new Yaml) + data (.load yaml ^String yaml-string)] + (java->clj data))) + +(defn extract-file-names-from-paths + "Given a sequence of java.nio.file.Path objects, return a lazy sequence of the file names of the file represented + by those paths. Example ['/foo/bar/baz.tmp'] will result in ['baz.tmp']" + [paths-to-files] + (map #(.toString (.getFileName %)) paths-to-files)) + +(defn remove-suffix-from-file-names + "Given a suffix, and a sequence of file-names, remove the suffix from the filenames" + [files suffix] + (let [suffix-size (count suffix)] + (map (fn [s] + (if (str/ends-with? s suffix) + (subs s 0 (- (count s) suffix-size)) + s)) + files))) \ No newline at end of file diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/jruby_request.clj puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/jruby_request.clj --- puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/jruby_request.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/jruby_request.clj 2024-01-15 23:29:55.000000000 +0000 @@ -2,11 +2,8 @@ (:import [clojure.lang IFn]) (:require [clojure.tools.logging :as log] [clj-semver.core :as semver] - [ring.util.response :as ring-response] [slingshot.slingshot :as sling] - [puppetlabs.ring-middleware.core :as middleware] [puppetlabs.ring-middleware.utils :as ringutils] - [puppetlabs.services.protocols.jruby-puppet :as jruby-puppet] [puppetlabs.services.protocols.jruby-metrics :as jruby-metrics] [puppetlabs.services.jruby.jruby-puppet-service :as jruby] [schema.core :as schema] diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/ringutils.clj puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/ringutils.clj --- puppetserver-7.9.5/src/clj/puppetlabs/puppetserver/ringutils.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/puppetserver/ringutils.clj 2024-01-15 23:29:55.000000000 +0000 @@ -29,9 +29,9 @@ ;;; Private (defn log-access-denied - [uri certificate] "Log a message to info stating that the client is not in the access control whitelist." + [uri certificate] (let [subject (ssl-utils/get-cn-from-x509-certificate certificate)] (log/info (format "%s\n%s" (i18n/trs "Client ''{0}'' access to {1} rejected;" subject uri) (i18n/trs "client not found in whitelist configuration."))))) @@ -75,6 +75,13 @@ (handler req) {:status 403 :body "Forbidden."}))) +(defn wrap-with-certname-as-compiler + "Function that returns middleware that add X-Puppet-Compiler-Name to the response, + only for the posts to the v3 catalog endpoint. Otherwise, do nothing." + [handler name] + (fn [request] + (ring/header (handler request) "X-Puppet-Compiler-Name" name))) + (defn wrap-with-puppet-version-header "Function that returns a middleware that adds an X-Puppet-Version header to the response." @@ -112,7 +119,7 @@ whitelist-settings :- (schema/maybe WhitelistSettings)] (let [handler-with-trapperkeeper-authorization (authorization-fn base-handler)] (if-let [handler-with-client-whitelist-authorization - (if (or (false? (:authorization-required whitelist-settings)) + (when (or (false? (:authorization-required whitelist-settings)) (not-empty (:client-whitelist whitelist-settings))) (wrap-with-cert-whitelist-check base-handler whitelist-settings))] (fn [request] diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/analytics/analytics_service.clj puppetserver-8.4.0/src/clj/puppetlabs/services/analytics/analytics_service.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/analytics/analytics_service.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/analytics/analytics_service.clj 2024-01-15 23:29:55.000000000 +0000 @@ -8,10 +8,23 @@ (defprotocol AnalyticsService "Protocol placeholder for the analytics service.") +(def analytics-service-job-group-id + :analytics-service-job-group) + +(defn safe-run-dropsonde + "Prevent exceptions from escaping as this is run in a scheduled task" + [config] + (try + (log/debug (i18n/trs "Running dropsonde")) + (run-dropsonde config) + (log/debug (i18n/trs "dropsonde run complete")) + (catch Exception _ + (log/info (i18n/trs "Failed while running dropsonde"))))) + (defservice analytics-service AnalyticsService [[:PuppetServerConfigService get-config] - [:SchedulerService interspaced]] + [:SchedulerService interspaced stop-jobs]] (start [this context] @@ -25,8 +38,13 @@ check-for-updates (get-in config [:product :check-for-updates] true)] (if check-for-updates (interspaced checkin-interval-millis - (fn [] (version-check/check-for-updates! - {:product-name product-name} update-server-url))) + (fn [] + (try + (version-check/check-for-update + {:product-name product-name} update-server-url) + (catch Exception _ + (log/error (i18n/trs "Failed to check for product updates"))))) + analytics-service-job-group-id) (log/info (i18n/trs "Not checking for updates - opt-out setting exists")))) (log/info (i18n/trs "Puppet Server Update Service has successfully started and will run in the background")) @@ -36,7 +54,12 @@ dropsonde-interval-millis (* 1000 (get-in config [:dropsonde :interval] (* 60 60 24 7)))] (if dropsonde-enabled - (interspaced dropsonde-interval-millis #(run-dropsonde config)) + (interspaced dropsonde-interval-millis #(safe-run-dropsonde config) analytics-service-job-group-id) (log/info (i18n/trs (str "Not submitting module metrics via Dropsonde -- submission is disabled. " "Enable this feature by setting `dropsonde.enabled` to true in Puppet Server''s config.")))))) - context)) + context) + + (stop [this context] + (log/info (i18n/trs "Puppet Server Update Service shutting down")) + (stop-jobs analytics-service-job-group-id) + context)) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/analytics/dropsonde.clj puppetserver-8.4.0/src/clj/puppetlabs/services/analytics/dropsonde.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/analytics/dropsonde.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/analytics/dropsonde.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,5 @@ (ns puppetlabs.services.analytics.dropsonde - (:require [clojure.java.shell :refer [sh]] - [clojure.tools.logging :as log] + (:require [clojure.tools.logging :as log] [puppetlabs.i18n.core :as i18n] [puppetlabs.puppetserver.shell-utils :as shell-utils] [puppetlabs.services.jruby.jruby-puppet-core :as jruby-puppet])) @@ -25,7 +24,7 @@ :env {"GEM_HOME" dropsonde-dir "GEM_PATH" dropsonde-dir "HOME" dropsonde-dir - "PUPPET_CONFDIF" confdir + "PUPPET_CONFDIR" confdir "PUPPET_CODEDIR" codedir "PUPPET_VARDIR" vardir "PUPPET_LOGDIR" logdir}})] diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/ca/certificate_authority_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/ca/certificate_authority_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/ca/certificate_authority_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/ca/certificate_authority_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,29 +1,33 @@ (ns puppetlabs.services.ca.certificate-authority-core - (:import [java.io InputStream ByteArrayInputStream] - (clojure.lang IFn) - (org.joda.time DateTime)) - (:require [puppetlabs.puppetserver.certificate-authority :as ca] - [puppetlabs.puppetserver.ringutils :as ringutils] - [puppetlabs.ring-middleware.core :as middleware] - [puppetlabs.ring-middleware.utils :as middleware-utils] - [puppetlabs.puppetserver.liberator-utils :as liberator-utils] - [puppetlabs.comidi :as comidi :refer [GET ANY PUT DELETE]] - [bidi.schema :as bidi-schema] - [slingshot.slingshot :as sling] - [clojure.tools.logging :as log] - [clojure.java.io :as io] - [clojure.string :as string] + (:require [bidi.schema :as bidi-schema] + [cheshire.core :as cheshire] [clj-time.core :as time] [clj-time.format :as time-format] - [schema.core :as schema] - [cheshire.core :as cheshire] + [clojure.java.io :as io] + [clojure.string :as string] + [clojure.tools.logging :as log] [liberator.core :refer [defresource]] [liberator.representation :as representation] + [puppetlabs.comidi :as comidi :refer [ANY DELETE GET POST PUT]] + [puppetlabs.i18n.core :as i18n] + [puppetlabs.puppetserver.certificate-authority :as ca] + [puppetlabs.puppetserver.common :as common] + [puppetlabs.puppetserver.liberator-utils :as liberator-utils] + [puppetlabs.puppetserver.ringutils :as ringutils] + [puppetlabs.ring-middleware.core :as middleware] + [puppetlabs.ring-middleware.utils :as middleware-utils] + [puppetlabs.ssl-utils.core :as utils] + [puppetlabs.trapperkeeper.authorization.ring :as ring] + [puppetlabs.trapperkeeper.authorization.ring-middleware :as auth-middleware] + [puppetlabs.trapperkeeper.services.status.status-core :as status-core] [ring.util.request :as request] [ring.util.response :as rr] - [puppetlabs.trapperkeeper.services.status.status-core :as status-core] - [puppetlabs.i18n.core :as i18n] - [puppetlabs.ssl-utils.core :as utils])) + [schema.core :as schema] + [slingshot.slingshot :refer [try+]]) + (:import (clojure.lang IFn) + (java.io ByteArrayInputStream InputStream StringWriter) + (java.security.cert X509Certificate) + (org.joda.time DateTime))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Constants @@ -34,14 +38,30 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; 'handler' functions for HTTP endpoints -;; Perhaps this could/should be in the tk context somewhere, and of -;; course it'll need to be used to guard any competing writes. -(def crl-write-serializer (Object.)) + +(schema/defn format-http-date :- (schema/maybe DateTime) + "Formats an http-date into joda time. Returns nil for malformed or nil + http-dates" + [http-date :- (schema/maybe schema/Str)] + (when http-date + (try + (time-format/parse + (time-format/formatters :rfc822) + (string/replace http-date #"GMT" "+0000")) + (catch IllegalArgumentException _ + nil)))) (defn handle-get-certificate - [subject {:keys [cacert signeddir]}] - (-> (if-let [certificate (ca/get-certificate subject cacert signeddir)] - (rr/response certificate) + [subject {:keys [cacert signeddir]} request] + (-> (if-let [certificate-path (ca/get-certificate-path subject cacert signeddir)] + (let [last-modified-val (rr/get-header request "If-Modified-Since") + last-modified-date-time (format-http-date last-modified-val) + cert-last-modified-date-time (ca/get-file-last-modified certificate-path)] + (if (or (nil? last-modified-date-time) + (time/after? cert-last-modified-date-time last-modified-date-time)) + (rr/response (slurp certificate-path)) + (-> (rr/response nil) + (rr/status 304)))) (rr/not-found (i18n/tru "Could not find certificate {0}" subject))) (rr/content-type "text/plain"))) @@ -52,81 +72,82 @@ (rr/not-found (i18n/tru "Could not find certificate_request {0}" subject))) (rr/content-type "text/plain"))) + (schema/defn handle-put-certificate-request! - [subject :- String - certificate-request :- InputStream - ca-settings :- ca/CaSettings] - (sling/try+ - (ca/process-csr-submission! subject certificate-request ca-settings) - (rr/content-type (rr/response nil) "text/plain") + [ca-settings :- ca/CaSettings + report-activity + {:keys [body] {:keys [subject]} :route-params :as request}] + (try+ + (let [report-activity-fn (ca/create-report-activity-fn report-activity request)] + (ca/process-csr-submission! subject body ca-settings report-activity-fn) + (rr/content-type (rr/response nil) "text/plain")) (catch ca/csr-validation-failure? {:keys [msg]} (log/error msg) ;; Respond to all CSR validation failures with a 400 (middleware-utils/plain-response 400 msg)))) -(schema/defn format-http-date :- (schema/maybe DateTime) - "Formats an http-date into joda time. Returns nil for malformed or nil - http-dates" - [http-date :- (schema/maybe schema/Str)] - (when http-date - (try - (time-format/parse - (time-format/formatters :rfc822) - (string/replace http-date #"GMT" "+0000")) - (catch IllegalArgumentException e - nil)))) + +(schema/defn resolve-crl-information + "Create a map that has the appropriate path, lock, timeout and descriptor for the crl being used" + [{:keys [enable-infra-crl cacrl infra-crl-path crl-lock crl-lock-timeout-seconds]} :- ca/CaSettings] + {:path (if (true? enable-infra-crl) infra-crl-path cacrl) + :lock crl-lock + :descriptor ca/crl-lock-descriptor + :timeout crl-lock-timeout-seconds}) (defn handle-get-certificate-revocation-list "Always return the crl if no 'If-Modified-Since' header is provided or if that header is not in correct http-date format. If the header is - present and has correct format, only return the crl if the master + present and has correct format, only return the crl if the server cacrl is newer than the agent crl." - [request {:keys [cacrl infra-crl-path enable-infra-crl]}] + [request ca-settings] (let [agent-crl-last-modified-val (rr/get-header request "If-Modified-Since") agent-crl-last-modified-date-time (format-http-date agent-crl-last-modified-val) - master-crl-to-use (if (true? enable-infra-crl) infra-crl-path cacrl) - master-crl-last-modified-date-time (ca/get-crl-last-modified master-crl-to-use)] - (if (or (nil? agent-crl-last-modified-date-time) - (time/after? master-crl-last-modified-date-time agent-crl-last-modified-date-time)) - (-> (ca/get-certificate-revocation-list master-crl-to-use) - (rr/response) - (rr/content-type "text/plain")) - (-> (rr/response nil) - (rr/status 304) - (rr/content-type "text/plain"))))) + {:keys [path lock descriptor timeout]} (resolve-crl-information ca-settings)] + ;; Since the locks are reentrant, obtain the read lock to prevent modification during the + ;; window of time between when the last-modified is read and when the crl content is potentially read. + (common/with-safe-read-lock lock descriptor timeout + (if (or (nil? agent-crl-last-modified-date-time) + (time/after? (ca/get-file-last-modified path lock descriptor timeout) + agent-crl-last-modified-date-time)) + (-> (ca/get-certificate-revocation-list path lock descriptor timeout) + (rr/response) + (rr/content-type "text/plain")) + (-> (rr/response nil) + (rr/status 304) + (rr/content-type "text/plain")))))) (schema/defn handle-put-certificate-revocation-list! [incoming-crl-pem :- InputStream - {:keys [cacrl cacert enable-infra-crl infra-crl-path]} :- ca/CaSettings] - (locking crl-write-serializer - (try - (let [byte-stream (-> incoming-crl-pem - ca/input-stream->byte-array - ByteArrayInputStream.) - incoming-crls (utils/pem->crls byte-stream)] - (if (empty? incoming-crls) - (do - (log/info (i18n/trs "No valid CRLs submitted, nothing will be updated.")) - (middleware-utils/plain-response 400 "No valid CRLs submitted.")) - - (do - (ca/update-crls incoming-crls cacrl cacert) - (when enable-infra-crl - (ca/update-crls incoming-crls infra-crl-path cacert)) - (middleware-utils/plain-response 200 "Successfully updated CRLs.")))) - (catch IllegalArgumentException e - (let [error-msg (.getMessage e)] - (log/error error-msg) - (middleware-utils/plain-response 400 error-msg)))))) + {:keys [cacrl cacert] :as ca-settings} :- ca/CaSettings] + (try + (let [byte-stream (-> incoming-crl-pem + ca/input-stream->byte-array + ByteArrayInputStream.) + incoming-crls (utils/pem->crls byte-stream)] + (if (empty? incoming-crls) + (do + (log/info (i18n/trs "No valid CRLs submitted, nothing will be updated.")) + (middleware-utils/plain-response 400 "No valid CRLs submitted.")) + (do + (ca/update-crls! incoming-crls cacrl cacert ca-settings) + (middleware-utils/plain-response 200 "Successfully updated CRLs.")))) + (catch IllegalArgumentException e + (let [error-msg (.getMessage e)] + (log/error error-msg) + (middleware-utils/plain-response 400 error-msg))))) (schema/defn handle-delete-certificate-request! [subject :- String ca-settings :- ca/CaSettings] (let [response (ca/delete-certificate-request! ca-settings subject) outcomes->codes {:success 204 :not-found 404 :error 500}] - (-> (rr/response (:message response)) - (rr/status ((response :outcome) outcomes->codes)) - (rr/content-type "text/plain")))) + (if (not= (response :outcome) :success) + (-> (rr/response (:message response)) + (rr/status ((response :outcome) outcomes->codes)) + (rr/content-type "text/plain")) + (-> (rr/response (:message response)) + (rr/status ((response :outcome) outcomes->codes)))))) (schema/defn handle-get-ca-expirations [ca-settings :- ca/CaSettings] @@ -143,10 +164,17 @@ (catch Exception e (log/debug e)))) +(schema/defn certificate-issued? :- schema/Bool + [settings :- ca/CaSettings + subject :- schema/Str] + (or (ca/certificate-exists? settings subject) + (ca/in-cert-inventory-file? settings subject))) + (schema/defn handle-cert-clean - [{:keys [body]} - ca-settings :- ca/CaSettings] - (if-let [json-body (try-to-parse body)] + [request + ca-settings :- ca/CaSettings + report-activity] + (if-let [json-body (try-to-parse (:body request))] ;; TODO support async mode (if (true? (:async json-body)) (-> (rr/response "Async mode is not currently supported.") @@ -155,18 +183,20 @@ (if-let [certnames (:certnames json-body)] (let [{existing-certs true missing-certs false} (group-by - #(ca/certificate-exists? ca-settings %) + #(certificate-issued? ca-settings %) certnames) message (when (seq missing-certs) (format "The following certs do not exist and cannot be revoked: %s" - (vec missing-certs)))] + (vec missing-certs))) + report-activity-fn (ca/create-report-activity-fn report-activity request)] (try - (ca/revoke-existing-certs! ca-settings existing-certs) + (ca/revoke-existing-certs! ca-settings existing-certs report-activity-fn) (ca/delete-certificates! ca-settings existing-certs) (-> (rr/response (or message "Successfully cleaned all certs.")) (rr/status 200) (rr/content-type "text/plain")) (catch Exception e + (log/info e (i18n/trs "Error while cleaning certs")) (-> (rr/response (str "Error while cleaning certs: " (.getMessage e))) (rr/status 500) (rr/content-type "text/plain"))))) @@ -177,6 +207,119 @@ (rr/status 400) (rr/content-type "text/plain")))) +(schema/defn validate-cert-in-infra-list :- schema/Bool + [request-cert :- X509Certificate + infra-nodes-path :- schema/Str] + (if infra-nodes-path + (let [subject-name (utils/get-cn-from-x509-certificate request-cert)] + (if (.exists (io/file infra-nodes-path)) + (with-open [infra-nodes-reader (io/reader infra-nodes-path)] + (let [infra-nodes (ca/read-infra-nodes infra-nodes-reader)] + (boolean (some #(= subject-name %) infra-nodes)))) + (do (log/info (i18n/trs "Unable to find infra-nodes file at {0}" infra-nodes-path)) + false))) + (do + (log/info (i18n/trs "infra-nodes-path is nil, cannot validate certificate is allowed.")) + false))) + +(schema/defn validate-header-cert-not-revoked :- (schema/maybe X509Certificate) + "Given a certificate, validate that the certificate is not in the CRL. The + messaging is specific to the header method of certificate delivery. If the + certificate is valid, it is returned, otherwise return nil" + [cert :- X509Certificate + ca-settings :- ca/CaSettings] + (if-not (ca/is-revoked? cert ca-settings) + cert + (log/warn (i18n/trs "Rejecting certificate request because the {0} was specified and the certificate is revoked." auth-middleware/header-cert-name)))) + +(schema/defn request->cert :- (schema/maybe X509Certificate) + "Pull the client certificate from the request. Response includes the + certificate as a java.security.cert.X509Certificate object or, if none + can be found, nil. allow-header-cert-info determines whether to try to + pull the certificate from an HTTP header (true) or from the certificate + provided during SSL session negotiation (false). + + If allow-header-cert-info is false, and the cert is present in both the header + and the request, validate that the cert in the request is in the infra list. + If it isn't in the infra list, log the issue and return nil. + If the header isn't set, return the cert from the request. + " + [request :- ring/Request + {:keys [allow-header-cert-info infra-nodes-path] :as ca-settings} :- ca/CaSettings] + (let [header-cert-val (get-in request [:headers auth-middleware/header-cert-name])] + (if allow-header-cert-info + (validate-header-cert-not-revoked (auth-middleware/header->cert header-cert-val) ca-settings) + (if-let [request-cert (:ssl-client-cert request)] + (if header-cert-val + (if (validate-cert-in-infra-list request-cert infra-nodes-path) + (validate-header-cert-not-revoked (auth-middleware/header->cert header-cert-val) ca-settings) + (log/warn (i18n/trs "Rejecting certificate request because the {0} header was specified, but the client making the request was not in the allow list." auth-middleware/header-cert-name))) + request-cert) + (log/warn (i18n/trs "Request is missing a certificate for an endpoint that requires a certificate.")))))) + +(schema/def Certnames [schema/Str]) + +(schema/defn handle-bulk-cert-signing + [request + ca-settings :- ca/CaSettings + report-activity] + (let [json-body (try-to-parse (:body request)) + certnames (:certnames json-body)] + (if-let [schema-error (schema/check Certnames certnames)] + (-> (rr/response (cheshire/generate-string {:kind :schema-violation + :submitted certnames + :error (str schema-error)})) + (rr/status 422) + (rr/content-type "application/json")) + (-> (rr/response (cheshire/generate-string (ca/sign-multiple-certificate-signing-requests! certnames ca-settings report-activity))) + (rr/status 200) + (rr/content-type "application/json"))))) + +(schema/defn handle-bulk-cert-signing-all + [ca-settings :- ca/CaSettings report-activity] + (let [csr-files (-> (ca/get-paths-to-all-certificate-requests (:csrdir ca-settings)) + (common/extract-file-names-from-paths) + (common/remove-suffix-from-file-names ".pem")) + results (ca/sign-multiple-certificate-signing-requests! csr-files ca-settings report-activity)] + (-> (rr/response (cheshire/generate-string results)) + (rr/status 200) + (rr/content-type "application/json")))) + +(schema/defn ^:always-validate + handle-cert-renewal + "Given a request and the CA settings, if there is a cert present in the request + (either in the ssl-client-cert property of the request, or as an x-client-cert + field in the header when allow-header-cert-info is set to true) and the cert in + the request is valid and signed by the this CA. then generate a renewed cert and + return it in the response body" + [request + {:keys [cacert cakey allow-auto-renewal] :as ca-settings} :- ca/CaSettings + report-activity] + (if allow-auto-renewal + (if-let [request-cert (request->cert request ca-settings)] + (let [signing-cert (utils/pem->ca-cert cacert cakey)] + (if (ca/cert-authority-id-match-ca-subject-id? request-cert signing-cert) + (do + (log/info (i18n/trs "Certificate present, processing renewal request")) + (let [cert-signing-result (ca/renew-certificate! request-cert ca-settings report-activity) + cert-writer (StringWriter.)] + ;; has side effect of writing to the writer + (utils/cert->pem! cert-signing-result cert-writer) + (-> (rr/response (.toString cert-writer)) + (rr/content-type "text/plain")))) + (do + (log/info (i18n/trs "Certificate present, but does not match signature")) + (-> (rr/response (i18n/tru "Certificate present, but does not match signature")) + (rr/status 403) + (rr/content-type "text/plain"))))) + (do + (log/info (i18n/trs "No valid certificate found in renewal request")) + (-> (rr/bad-request (i18n/tru "No valid certificate found in renewal request")) + (rr/content-type "text/plain")))) + (-> (rr/response "Not Found") + (rr/status 404) + (rr/content-type "text/plain")))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Web app @@ -187,7 +330,7 @@ [true {::malformed message}]) (defn conflict - "Returns a value indicating to liberator that the request is is conflict + "Returns a value indicating to liberator that the request is conflict with the server, with the given error message assoc'ed into the context." [message] [true {::conflict message}]) @@ -263,7 +406,7 @@ (representation/ring-response))) (defresource certificate-status - [subject settings] + [subject settings report-activity] :allowed-methods [:get :put :delete] :available-media-types media-types @@ -276,7 +419,7 @@ (case desired-state :revoked ;; A signed cert must exist if we are to revoke it. - (when-not (ca/certificate-exists? settings subject) + (when-not (certificate-issued? settings subject) (conflict (i18n/tru "Cannot revoke certificate for host {0} without a signed certificate" subject))) :signed @@ -290,15 +433,15 @@ (conflict error-message)))))) :delete! - (fn [context] + (fn [_context] (ca/delete-certificate! settings subject) (ca/delete-certificate-request! settings subject)) :exists? - (fn [context] + (fn [_context] (or - (ca/certificate-exists? settings subject) - (ca/csr-exists? settings subject))) + (certificate-issued? settings subject) + (ca/csr-exists? settings subject))) :handle-conflict (fn [context] @@ -366,12 +509,14 @@ :put! (fn [context] - (let [desired-state (get-desired-state context)] - (locking crl-write-serializer - (ca/set-certificate-status! - (merge-request-settings settings context) - subject - desired-state)) + (let [desired-state (get-desired-state context) + request (:request context) + report-activity-fn (ca/create-report-activity-fn report-activity request)] + (ca/set-certificate-status! + (merge-request-settings settings context) + subject + desired-state + report-activity-fn) (-> context (assoc-in [:representation :media-type] "text/plain"))))) @@ -395,32 +540,39 @@ (as-json-or-pson context))))) (schema/defn ^:always-validate web-routes :- bidi-schema/RoutePair - [ca-settings :- ca/CaSettings] + [ca-settings :- ca/CaSettings + report-activity] (comidi/routes (comidi/context ["/v1"] (ANY ["/certificate_status/" :subject] [subject] - (certificate-status subject ca-settings)) + (certificate-status subject ca-settings report-activity)) (comidi/context ["/certificate_statuses/"] (ANY [[#"[^/]+" :ignored-but-required]] request (certificate-statuses request ca-settings)) (ANY [""] [] (middleware-utils/plain-response 400 "Missing URL Segment"))) - (GET ["/certificate/" :subject] [subject] - (handle-get-certificate subject ca-settings)) + (GET ["/certificate/" :subject] request + (handle-get-certificate (get-in request [:params :subject]) ca-settings request)) (comidi/context ["/certificate_request/" :subject] (GET [""] [subject] (handle-get-certificate-request subject ca-settings)) - (PUT [""] [subject :as {body :body}] - (handle-put-certificate-request! subject body ca-settings)) + (PUT [""] request + (handle-put-certificate-request! ca-settings report-activity request)) (DELETE [""] [subject] (handle-delete-certificate-request! subject ca-settings))) (GET ["/certificate_revocation_list/" :ignored-node-name] request (handle-get-certificate-revocation-list request ca-settings)) (PUT ["/certificate_revocation_list"] request (handle-put-certificate-revocation-list! (:body request) ca-settings)) - (GET ["/expirations"] request + (GET ["/expirations"] _request (handle-get-ca-expirations ca-settings)) (PUT ["/clean"] request - (handle-cert-clean request ca-settings))) + (handle-cert-clean request ca-settings report-activity)) + (POST ["/certificate_renewal"] request + (handle-cert-renewal request ca-settings report-activity)) + (POST ["/sign"] request + (handle-bulk-cert-signing request ca-settings report-activity)) + (POST ["/sign/all"] _request + (handle-bulk-cert-signing-all ca-settings report-activity))) (comidi/not-found "Not Found"))) (schema/defn ^:always-validate @@ -448,7 +600,7 @@ ;; and replace with a line chaining the handler into a call to ;; 'authorization-fn'. (let [whitelist-path (str path - (if (not= \/ (last path)) "/") + (when (not= \/ (last path)) "/") puppet-ca-API-version "/certificate_status")] (-> route-handler @@ -463,6 +615,6 @@ ;;; Public (schema/defn ^:always-validate v1-status :- status-core/StatusCallbackResponse - [level :- status-core/ServiceStatusDetailLevel] + [_level :- status-core/ServiceStatusDetailLevel] {:state :running :status {}}) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/ca/certificate_authority_service.clj puppetserver-8.4.0/src/clj/puppetlabs/services/ca/certificate_authority_service.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/ca/certificate_authority_service.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/ca/certificate_authority_service.clj 2024-01-15 23:29:55.000000000 +0000 @@ -2,22 +2,40 @@ (:require [clojure.tools.logging :as log] [me.raynes.fs :as fs] [puppetlabs.trapperkeeper.core :as tk] - [puppetlabs.trapperkeeper.services :as tk-services] + [puppetlabs.trapperkeeper.services :refer [maybe-get-service] :as tk-services] [puppetlabs.trapperkeeper.services.protocols.filesystem-watch-service :as watch-protocol] [puppetlabs.puppetserver.certificate-authority :as ca] [puppetlabs.services.ca.certificate-authority-core :as core] [puppetlabs.services.protocols.ca :refer [CaService]] [puppetlabs.comidi :as comidi] [puppetlabs.i18n.core :as i18n] - [puppetlabs.trapperkeeper.services.status.status-core :as status-core])) + [puppetlabs.trapperkeeper.services.status.status-core :as status-core] + [puppetlabs.rbac-client.protocols.activity :refer [ActivityReportingService] :as activity-proto])) + +(def one-day-ms + (* 24 60 60 1000)) + +(def ca-scheduled-job-group-id + :ca-scheduled-job-group-id) + +(defn evaluate-crls-for-expiration + [ca-settings] + (try + ;; don't allow exceptions to escape + (ca/maybe-update-crls-for-expiration ca-settings) + (catch Exception e + (log/error e (i18n/trs "Failed to evaluate crls for expiration"))))) (tk/defservice certificate-authority-service CaService - [[:PuppetServerConfigService get-config get-in-config] - [:WebroutingService add-ring-handler get-route] - [:AuthorizationService wrap-with-authorization-check] - [:FilesystemWatchService create-watcher] - [:StatusService register-status]] + {:required + [[:PuppetServerConfigService get-config get-in-config] + [:WebroutingService add-ring-handler get-route] + [:AuthorizationService wrap-with-authorization-check] + [:FilesystemWatchService create-watcher] + [:StatusService register-status] + [:SchedulerService interspaced stop-jobs]] + :optional [ActivityReportingService]} (init [this context] (let [path (get-route this) @@ -31,14 +49,24 @@ host-crl-file (.getCanonicalPath (fs/file (get-in-config [:puppetserver :hostcrl]))) infra-nodes-file (.getCanonicalPath (fs/file (str (fs/parent ca-crl-file) "/infra_inventory.txt"))) - watcher (create-watcher {:recursive false})] + watcher (create-watcher {:recursive false}) + report-activity (if-let [activity-reporting-service (maybe-get-service this :ActivityReportingService)] + (fn [& payload] + (try + (activity-proto/report-activity! activity-reporting-service (first payload)) + (catch Exception e + (log/error + (i18n/trs "Reporting CA event failed with: {0}\nPayload: {1}" + (.getMessage e) + (first payload)))))) + (constantly nil))] (ca/validate-settings! settings) (ca/initialize! settings) (log/info (i18n/trs "CA Service adding a ring handler")) (add-ring-handler this (core/get-wrapped-handler - (-> (core/web-routes settings) + (-> (core/web-routes settings report-activity) ((partial comidi/context path)) comidi/routes->handler) settings @@ -66,14 +94,24 @@ (= (.getCanonicalPath (:changed-path %)) infra-nodes-file)) events) - (ca/generate-infra-serials settings)))) + (ca/generate-infra-serials! settings)))) (register-status "ca" (status-core/get-artifact-version "puppetlabs" "puppetserver") 1 - (partial core/v1-status)) + core/v1-status) (assoc context :auth-handler auth-handler - :watcher watcher))) + :watcher watcher + :ca-settings settings))) + (start [this context] + (log/info (i18n/trs "Starting CA service")) + (interspaced one-day-ms #(evaluate-crls-for-expiration (:ca-settings context)) ca-scheduled-job-group-id) + context) + + (stop [this context] + (log/info (i18n/trs "Stopping CA service")) + (stop-jobs ca-scheduled-job-group-id) + (dissoc context :ca-settings)) (initialize-master-ssl! [this master-settings certname] diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/config/puppet_server_config_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/config/puppet_server_config_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/config/puppet_server_config_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/config/puppet_server_config_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -6,7 +6,6 @@ [clojure.tools.logging :as log] [puppetlabs.kitchensink.core :refer [keyset]] [puppetlabs.services.jruby.jruby-puppet-service :as jruby] - [puppetlabs.services.jruby.jruby-puppet-service :as jruby] [schema.core :as schema] [clojure.string :as str] [puppetlabs.i18n.core :as i18n]) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/jruby_metrics_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/jruby_metrics_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/jruby_metrics_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/jruby_metrics_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -12,7 +12,6 @@ [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol]) (:import (com.codahale.metrics MetricRegistry Gauge Counter Histogram Meter Timer) (clojure.lang Atom IFn) - (puppetlabs.services.jruby_pool_manager.jruby_schemas JRubyInstance) (java.util.concurrent TimeUnit) (org.joda.time DateTime) (org.joda.time.format DateTimeFormatter) @@ -208,7 +207,7 @@ ;; TODO: v4.0 of Dropwizard Metrics has a MetricFilter/startsWith ;; static method that returns a filter which can be passed ;; directly to .getTimers. - metric-filter (partial filter (fn [[k v]] + metric-filter (partial filter (fn [[k _]] (str/starts-with? k metric-namespace)))] (->> (.getTimers metric-registry) metric-filter diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/jruby_puppet_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,5 @@ (ns puppetlabs.services.jruby.jruby-puppet-core (:require [clojure.tools.logging :as log] - [me.raynes.fs :as fs] [schema.core :as schema] [puppetlabs.kitchensink.core :as ks] [puppetlabs.services.jruby.jruby-puppet-schemas :as jruby-puppet-schemas] @@ -9,13 +8,11 @@ [puppetlabs.services.jruby.puppet-environments :as puppet-env] [puppetlabs.trapperkeeper.services.protocols.metrics :as metrics] [puppetlabs.i18n.core :as i18n] - [clojure.tools.logging :as log] [clojure.string :as str]) (:import (com.puppetlabs.puppetserver PuppetProfiler JRubyPuppet) (clojure.lang IFn) (java.util HashMap) - (com.codahale.metrics MetricRegistry) - (org.jruby.runtime Constants))) + (com.codahale.metrics MetricRegistry))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Constants @@ -85,9 +82,9 @@ [:server-var-dir "vardir"] [:server-run-dir "rundir"] [:server-log-dir "logdir"]]] - (if-let [value (get config setting-name)] + (when-let [value (get config setting-name)] (.put puppet-config dir (ks/absolute-path value)))) - (if (:disable-i18n config) + (when (:disable-i18n config) ; The value for disable-i18n is stripped in Puppet::Server::PuppetConfig ; so that only the key is outputted, so that '--disable_18n' is used, not ; '--disable_i18n true' @@ -183,7 +180,7 @@ [http-config :- {schema/Keyword schema/Any} jruby-puppet-config :- {schema/Keyword schema/Any} multithreaded :- schema/Bool] - (if multithreaded + (when multithreaded (log/info (i18n/trs "Disabling i18n for puppet because using multithreaded jruby"))) (let [config (-> jruby-puppet-config (assoc :http-client-ssl-protocols (:ssl-protocols http-config)) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/jruby_puppet_schemas.clj puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/jruby_puppet_schemas.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/jruby_puppet_schemas.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/jruby_puppet_schemas.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,5 @@ (ns puppetlabs.services.jruby.jruby-puppet-schemas - (:require [schema.core :as schema] - [puppetlabs.services.jruby-pool-manager.jruby-schemas :as jruby-schemas])) + (:require [schema.core :as schema])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Schemas diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/puppet_environments.clj puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/puppet_environments.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/jruby/puppet_environments.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/jruby/puppet_environments.clj 2024-01-15 23:29:55.000000000 +0000 @@ -17,17 +17,17 @@ (reduce mark-expired! m (keys m)))] (reify EnvironmentRegistry - (registerEnvironment [this env-name] + (registerEnvironment [_this env-name] (when-not env-name (throw (IllegalArgumentException. (i18n/trs "Missing environment name!")))) (log/debug (i18n/trs "Registering environment ''{0}''" env-name)) (swap! state assoc-in [(keyword env-name) :expired] false) nil) - (isExpired [this env-name] + (isExpired [_this env-name] (when-not env-name (throw (IllegalArgumentException. (i18n/trs "Missing environment name!")))) (get-in @state [(keyword env-name) :expired] true)) - (removeEnvironment [this env-name] + (removeEnvironment [_this env-name] (when-not env-name (throw (IllegalArgumentException. (i18n/trs "Missing environment name!")))) (log/debug (i18n/trs "Removing environment ''{0}'' from registry" env-name)) @@ -35,10 +35,10 @@ nil) EnvironmentStateContainer - (environment-state [this] state) - (mark-all-environments-expired! [this] + (environment-state [_this] state) + (mark-all-environments-expired! [_this] (log/info (i18n/trs "Marking all registered environments as expired.")) (swap! state mark-all-expired!)) - (mark-environment-expired! [this env-name] + (mark-environment-expired! [_this env-name] (log/info (i18n/trs "Marking environment ''{0}'' as expired." env-name)) (swap! state mark-expired! (keyword env-name)))))) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/legacy_routes/legacy_routes_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/legacy_routes/legacy_routes_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/legacy_routes/legacy_routes_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/legacy_routes/legacy_routes_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,11 +1,8 @@ (ns puppetlabs.services.legacy-routes.legacy-routes-core (:require [clojure.string :as str] - [clojure.tools.logging :as log] [puppetlabs.comidi :as comidi] [ring.util.codec :as ring-codec] [ring.util.response :as ring-response] - [compojure.route :as route] - [clojure.tools.logging :as log] [schema.core :as schema]) (:import (clojure.lang IFn))) @@ -99,8 +96,8 @@ (let [{{environment :environment} :params uri :uri query-string :query-string} request - path-info (str "/" (-> (str/split uri #"/" 3) (nth 2)))] - (let [compat-request + path-info (str "/" (-> (str/split uri #"/" 3) (nth 2))) + compat-request (-> request (map-accept-header) (assoc :path-info (str "/" api-version path-info) @@ -111,7 +108,7 @@ "environment" environment) query-string)) (dissoc :params :route-params))] - (handler compat-request)))) + (handler compat-request))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Routing diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/legacy_routes/legacy_routes_service.clj puppetserver-8.4.0/src/clj/puppetlabs/services/legacy_routes/legacy_routes_service.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/legacy_routes/legacy_routes_service.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/legacy_routes/legacy_routes_service.clj 2024-01-15 23:29:55.000000000 +0000 @@ -35,7 +35,7 @@ jruby-service (tk-services/get-service this :JRubyPuppetService) master-routes (comidi/context path (master-core/root-routes handle-request - (partial identity) + identity jruby-service identity (fn [_ _ _] @@ -45,7 +45,8 @@ false nil nil - nil)) + nil + (get-in config [:puppetserver :certname]))) master-route-handler (comidi/routes->handler master-routes) master-mount (master-core/get-master-mount master-ns @@ -65,8 +66,7 @@ real-ca-service? (= (namespace (tk-services/service-symbol ca-service)) "puppetlabs.services.ca.certificate-authority-service") ca-settings (ca/config->ca-settings (get-config)) - ca-route-handler (-> ca-settings - (ca-core/web-routes) + ca-route-handler (-> (ca-core/web-routes ca-settings nil) ((partial comidi/context path)) comidi/routes->handler) ca-handler-info (when diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/master/file_serving.clj puppetserver-8.4.0/src/clj/puppetlabs/services/master/file_serving.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/master/file_serving.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/master/file_serving.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,11 +1,11 @@ (ns puppetlabs.services.master.file-serving (:require [bidi.bidi :as bidi] - [clj-yaml.core :as yaml] [clojure.java.io :as io] [clojure.string :as str] [digest :as digest] [me.raynes.fs :as fs] [puppetlabs.i18n.core :as i18n] + [puppetlabs.puppetserver.common :as common] [puppetlabs.ring-middleware.utils :as middleware-utils] [ring.util.response :as rr]) (:import com.sun.security.auth.module.UnixSystem @@ -150,7 +150,7 @@ [project-dir] (let [config-path (str project-dir "/bolt-project.yaml")] (when (fs/file? config-path) - (yaml/parse-string (slurp config-path))))) + (common/parse-yaml (slurp config-path))))) (defn parse-modulepath "The modulepath for a bolt project can either be an array of paths or a string diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/master/master_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/master/master_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/master/master_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/master/master_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -323,8 +323,7 @@ tasks for all environments can be requested, and a given task will list all the environments it is found in." [info-from-jruby :- [{schema/Any schema/Any}] - environment :- schema/Str - jruby-service :- (schema/protocol jruby-protocol/JRubyPuppetService)] + environment :- schema/Str] (let [format-task (fn [task-object] {:name (:name task-object) :private (get-in task-object [:metadata :private] false) @@ -484,8 +483,7 @@ request) environment))] (all-tasks-response! task-info-for-env - environment - jruby-service) + environment) (environment-not-found environment))))) (schema/defn ^:always-validate @@ -602,8 +600,7 @@ tasks for all environments can be requested, and a given task will list all the environments it is found in." [info-from-jruby :- [{schema/Any schema/Any}] - environment :- schema/Str - jruby-service :- (schema/protocol jruby-protocol/JRubyPuppetService)] + environment :- schema/Str] (let [format-plan (fn [plan-object] {:name (:name plan-object) :environment [{:name environment @@ -625,8 +622,7 @@ request) environment))] (all-plans-response! plan-info-for-env - environment - jruby-service) + environment) (environment-not-found environment))))) (schema/defn ^:always-validate @@ -634,7 +630,6 @@ "Fills in a bare PlanData map by examining the files it refers to, returning PlanDetails." [plan-data :- PlanData - env-name :- schema/Str module-name :- schema/Str plan-name :- schema/Str] (if (:error plan-data) @@ -660,7 +655,7 @@ module-name plan-name) sort-nested-info-maps - (plan-data->plan-details environment-name module-name plan-name))) + (plan-data->plan-details module-name plan-name))) (schema/defn ^:always-validate plan-details-fn :- IFn @@ -912,7 +907,7 @@ (schema/required-key "code_ast") schema/Str (schema/required-key "trusted_facts") {(schema/required-key "values") {schema/Str schema/Any}} (schema/required-key "facts") {(schema/required-key "values") {schema/Str schema/Any}} - (schema/required-key "variables") {(schema/required-key "values") (schema/either [{schema/Str schema/Any}] {schema/Str schema/Any})} + (schema/required-key "variables") {(schema/required-key "values") (schema/cond-pre [{schema/Str schema/Any}] {schema/Str schema/Any})} ;; Both environment and versioned project are technically listed in the schema as ;; "optional" but we will check later that exactly one of them is set. (schema/optional-key "environment") schema/Str @@ -1056,7 +1051,8 @@ "v3 route tree for the ruby side of the master service." [request-handler :- IFn bolt-builtin-content-dir :- (schema/maybe [schema/Str]) - bolt-projects-dir :- (schema/maybe schema/Str)] + bolt-projects-dir :- (schema/maybe schema/Str) + certname :- schema/Str] (comidi/routes (comidi/GET ["/node/" [#".*" :rest]] request (request-handler request)) @@ -1077,7 +1073,8 @@ (comidi/GET ["/catalog/" [#".*" :rest]] request (request-handler (assoc request :include-code-id? true))) (comidi/POST ["/catalog/" [#".*" :rest]] request - (request-handler (assoc request :include-code-id? true))) + (let [request-handler (ringutils/wrap-with-certname-as-compiler request-handler certname)] + (request-handler (assoc request :include-code-id? true)))) (comidi/PUT ["/facts/" [#".*" :rest]] request (request-handler request)) (comidi/PUT ["/report/" [#".*" :rest]] request @@ -1157,16 +1154,16 @@ jruby-service :- (schema/protocol jruby-protocol/JRubyPuppetService) wrap-with-jruby-queue-limit :- IFn current-code-id-fn :- IFn] - (let [v4-catalog-handler (v4-catalog-handler - jruby-service - wrap-with-jruby-queue-limit - current-code-id-fn)] + (let [v4-catalog-handler' (v4-catalog-handler + jruby-service + wrap-with-jruby-queue-limit + current-code-id-fn)] (comidi/context "/v4" (comidi/wrap-routes (comidi/routes (comidi/POST "/catalog" request - (v4-catalog-handler request))) + (v4-catalog-handler' request))) clojure-request-wrapper)))) (schema/defn ^:always-validate @@ -1183,9 +1180,10 @@ wrap-with-jruby-queue-limit :- IFn boltlib-path :- (schema/maybe [schema/Str]) bolt-builtin-content-dir :- (schema/maybe [schema/Str]) - bolt-projects-dir :- (schema/maybe schema/Str)] + bolt-projects-dir :- (schema/maybe schema/Str) + certname :- schema/Str] (comidi/context "/v3" - (v3-ruby-routes ruby-request-handler bolt-builtin-content-dir bolt-projects-dir) + (v3-ruby-routes ruby-request-handler bolt-builtin-content-dir bolt-projects-dir certname) (comidi/wrap-routes (v3-clojure-routes jruby-service get-code-content-fn @@ -1223,8 +1221,8 @@ [] (when-let [meminfo-file-content (meminfo-content)] (let [heap-size max-heap-size - mem-size (Integer. (second (re-find #"MemTotal:\s+(\d+)\s+\S+" - meminfo-file-content))) + mem-size (Integer/parseInt (second (re-find #"MemTotal:\s+(\d+)\s+\S+" + meminfo-file-content))) required-mem-size (* heap-size 1.1)] (when (< mem-size required-mem-size) (throw (Error. @@ -1297,7 +1295,8 @@ environment-class-cache-enabled :- schema/Bool boltlib-path :- (schema/maybe [schema/Str]) bolt-builtin-content-dir :- (schema/maybe [schema/Str]) - bolt-projects-dir :- (schema/maybe schema/Str)] + bolt-projects-dir :- (schema/maybe schema/Str) + certname :- schema/Str] (comidi/routes (v3-routes ruby-request-handler clojure-request-wrapper @@ -1308,7 +1307,8 @@ wrap-with-jruby-queue-limit boltlib-path bolt-builtin-content-dir - bolt-projects-dir) + bolt-projects-dir + certname) (v4-routes clojure-request-wrapper jruby-service wrap-with-jruby-queue-limit @@ -1371,7 +1371,8 @@ environment-class-cache-enabled :- schema/Bool boltlib-path :- (schema/maybe [schema/Str]) bolt-builtin-content-dir :- (schema/maybe [schema/Str]) - bolt-projects-dir :- (schema/maybe schema/Str)] + bolt-projects-dir :- (schema/maybe schema/Str) + certname :- schema/Str] (let [ruby-request-handler (wrap-middleware handle-request wrap-with-authorization-check puppet-version) @@ -1389,7 +1390,8 @@ environment-class-cache-enabled boltlib-path bolt-builtin-content-dir - bolt-projects-dir))) + bolt-projects-dir + certname))) (def MasterStatusV1 {(schema/optional-key :experimental) {:http-metrics [http-metrics/RouteSummary] diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/master/master_service.clj puppetserver-8.4.0/src/clj/puppetlabs/services/master/master_service.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/master/master_service.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/master/master_service.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,13 +1,10 @@ (ns puppetlabs.services.master.master-service (:require [clojure.tools.logging :as log] - [ring.middleware.params :as ring] [puppetlabs.trapperkeeper.core :refer [defservice]] - [puppetlabs.services.master.master-core :as core] [puppetlabs.puppetserver.certificate-authority :as ca] [puppetlabs.puppetserver.jruby-request :as jruby-request] [puppetlabs.trapperkeeper.services :as tk-services] [puppetlabs.comidi :as comidi] - [puppetlabs.dujour.version-check :as version-check] [puppetlabs.metrics.http :as http-metrics] [puppetlabs.services.protocols.master :as master] [puppetlabs.i18n.core :as i18n] @@ -107,15 +104,14 @@ [:CaService initialize-master-ssl! retrieve-ca-cert! retrieve-ca-crl! get-auth-handler] [:JRubyPuppetService] [:AuthorizationService wrap-with-authorization-check] - [:SchedulerService interspaced] [:StatusService register-status] [:VersionedCodeService get-code-content current-code-id]] (init [this context] - (core/validate-memory-requirements!) + (master-core/validate-memory-requirements!) (let [config (get-config) - route-config (core/get-master-route-config ::master-service config) - path (core/get-master-mount ::master-service route-config) + route-config (master-core/get-master-route-config ::master-service config) + path (master-core/get-master-mount ::master-service route-config) certname (get-in config [:puppetserver :certname]) localcacert (get-in config [:puppetserver :localcacert]) puppet-version (get-in config [:puppetserver :puppet-version]) @@ -142,7 +138,7 @@ boltlib-path (get-in config [:jruby-puppet :boltlib-path]) ring-app (comidi/routes - (core/construct-root-routes puppet-version + (master-core/construct-root-routes puppet-version jruby-service get-code-content current-code-id @@ -152,7 +148,8 @@ environment-class-cache-enabled boltlib-path bolt-builtin-content-dir - bolt-projects-dir)) + bolt-projects-dir + certname)) routes (comidi/context path ring-app) route-metadata (comidi/route-metadata routes) comidi-handler (comidi/routes->handler routes) @@ -171,7 +168,7 @@ (retrieve-ca-crl! hostcrl) (initialize-master-ssl! settings certname) - (core/register-jvm-metrics! registry metrics-server-id) + (master-core/register-jvm-metrics! registry metrics-server-id) (update-registry-settings :puppetserver {:default-metrics-allowed default-metrics-allowed}) @@ -200,12 +197,12 @@ "master" (status-core/get-artifact-version "puppetlabs" "puppetserver") master-service-status-version - (partial core/v1-status http-metrics http-client-metric-ids-for-status registry)) + (partial master-core/v1-status http-metrics http-client-metric-ids-for-status registry)) (register-status "server" (status-core/get-artifact-version "puppetlabs" "puppetserver") master-service-status-version - (partial core/v1-status http-metrics http-client-metric-ids-for-status registry)) + (partial master-core/v1-status http-metrics http-client-metric-ids-for-status registry)) (-> context (assoc :http-metrics http-metrics) (assoc :http-client-metric-ids-for-status http-client-metric-ids-for-status)))) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/puppet_admin/puppet_admin_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/puppet_admin/puppet_admin_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/puppet_admin/puppet_admin_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/puppet_admin/puppet_admin_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,7 +1,6 @@ (ns puppetlabs.services.puppet-admin.puppet-admin-core (:import (clojure.lang IFn)) - (:require [puppetlabs.kitchensink.core :as ks] - [puppetlabs.puppetserver.ringutils :as ringutils] + (:require [puppetlabs.puppetserver.ringutils :as ringutils] [puppetlabs.services.protocols.jruby-puppet :as jruby-puppet] [puppetlabs.ring-middleware.utils :as middleware-utils] [puppetlabs.puppetserver.liberator-utils :as liberator-utils] @@ -48,7 +47,7 @@ :new? false :delete! - (fn [context] + (fn [_context] (if env-name (jruby-puppet/mark-environment-expired! jruby-service env-name) (jruby-puppet/mark-all-environments-expired! jruby-service)))) @@ -77,8 +76,8 @@ ;;; Routing (defn v1-routes - [jruby-service] "Routes for v1 of the Puppet Admin HTTP API." + [jruby-service] (comidi/routes (comidi/ANY "/environment-cache" request (environment-cache-resource jruby-service diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/puppet_profiler/puppet_profiler_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/puppet_profiler/puppet_profiler_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/puppet_profiler/puppet_profiler_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/puppet_profiler/puppet_profiler_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,14 +1,11 @@ (ns puppetlabs.services.puppet-profiler.puppet-profiler-core - (:import (com.codahale.metrics MetricRegistry Timer) + (:import (com.codahale.metrics MetricRegistry) (com.puppetlabs.puppetserver MetricsPuppetProfiler PuppetProfiler)) (:require [clojure.string :as str] [schema.core :as schema] [puppetlabs.kitchensink.core :as ks] - [clojure.tools.logging :as log] [puppetlabs.trapperkeeper.services.status.status-core :as status-core] - [puppetlabs.metrics :as metrics] - [puppetlabs.i18n.core :refer [trs]])) - + [puppetlabs.metrics :as metrics])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Schemas diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/request_handler/request_handler_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/request_handler/request_handler_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/request_handler/request_handler_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/request_handler/request_handler_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -4,6 +4,7 @@ (com.puppetlabs.puppetserver JRubyPuppetResponse)) (:require [clojure.tools.logging :as log] [clojure.string :as string] + [clojure.walk :as walk] [puppetlabs.ring-middleware.utils :as ringutils] [puppetlabs.ssl-utils.core :as ssl-utils] [puppetlabs.puppetserver.common :as ps-common] @@ -23,9 +24,9 @@ "x-client-cert") (defn unmunge-http-header-name - [setting] "Given the value of a Puppet setting which contains a munged HTTP header name, convert it to the actual header name in all lower-case." + [setting] (->> (string/split setting #"_") rest (string/join "-") @@ -45,48 +46,31 @@ "Converts a JRubyPuppetResponse instance to a map." [response] { :pre [(instance? JRubyPuppetResponse response)] - :post [(map? %)] } - { :status (.getStatus response) - :body (.getBody response) - :headers {"Content-Type" (.getContentType response) - "X-Puppet-Version" (.getPuppetVersion response)}}) - -(defn body-for-jruby - "Converts the body from a request into a String if it is a non-binary - content type. Otherwise, just returns back the same body InputStream. - Non-binary request bodies are coerced per the appropriate encoding at - the Clojure layer. Binary request bodies, however, need to be preserved - in the originating InputStream so that they can be converted at the Ruby - layer, where the raw bytes within the stream can be converted losslessly - to a Ruby ASCII-8BIT encoded String. Java has no equivalent to ASCII-8BIT - for its Strings." + :post [(map? %)]} + { :status (.getStatus response) + :body (.getBody response) + :headers {"Content-Type" (.getContentType response) + "X-Puppet-Version" (.getPuppetVersion response)}}) + + +(defn update-body-for-jruby + "Converts the body from a request into a String if it is a form encoding. + Otherwise, just returns back the same body InputStream." [request] - (let [body (:body request) - content-type (if-let [raw-type (:content-type request)] + (let [content-type (when-let [raw-type (:content-type request)] (string/lower-case raw-type))] - (case content-type - (nil "" "application/octet-stream" "application/x-msgpack") body - ; Treatment of the *default* encoding arguably should be much more - ; intelligent than just choosing UTF-8. Basing the default on the - ; Content-Type would be an improvement although even this could lead to - ; some ambiguities. For "text/*" Content-Types, for example, - ; different RFCs specified that either US-ASCII or ISO-8859-1 could - ; be applied - see https://tools.ietf.org/html/rfc6657. Ideally, this - ; should be filled in with a broader list of the different Content-Types - ; that Puppet recognizes and the default encodings to use when typical - ; Puppet requests do not specify a corresponding charset. - (slurp body :encoding (or (:character-encoding request) - "UTF-8"))))) - + (if (= content-type "application/x-www-form-urlencoded") + (update request :body slurp :encoding (or (:character-encoding request) + "UTF-8")) + request))) (defn wrap-params-for-jruby "Pull parameters from the URL query string and/or urlencoded form POST body into the ring request map. Includes some special processing for a request destined for JRubyPuppet." [request] - (let [body-for-jruby (body-for-jruby request)] - (-> request - (assoc :body body-for-jruby) - pl-ring-params/params-request))) + (-> request + update-body-for-jruby + pl-ring-params/params-request)) (def unauthenticated-client-info "Return a map with default info for an unauthenticated client" @@ -97,19 +81,18 @@ "Return a map with authentication info based on header content" [header-dn-name header-dn-val header-auth-name header-auth-val] (if (ssl-utils/valid-x500-name? header-dn-val) - (do - (let [cn (ssl-utils/x500-name->CN header-dn-val) - authenticated (= "SUCCESS" header-auth-val)] - (log/debug (i18n/trs "CN ''{0}'' provided by HTTP header ''{1}''" - cn header-dn-val)) - (log/debug (format "%s %s" - (i18n/trs "Verification of client ''{0}'' provided by HTTP header ''{1}'': ''{2}''." - cn - header-auth-name - header-auth-val) - (i18n/trs "Authenticated: {0}." authenticated))) - {:client-cert-cn cn - :authenticated authenticated})) + (let [cn (ssl-utils/x500-name->CN header-dn-val) + authenticated (= "SUCCESS" header-auth-val)] + (log/debug (i18n/trs "CN ''{0}'' provided by HTTP header ''{1}''" + cn header-dn-val)) + (log/debug (format "%s %s" + (i18n/trs "Verification of client ''{0}'' provided by HTTP header ''{1}'': ''{2}''." + cn + header-auth-name + header-auth-val) + (i18n/trs "Authenticated: {0}." authenticated))) + {:client-cert-cn cn + :authenticated authenticated}) (do (if (nil? header-dn-val) (log/debug (format "%s %s" @@ -145,7 +128,7 @@ "Return an X509Certificate or nil from a string encoded for transmission in an HTTP header." [header-cert-val] - (if header-cert-val + (when header-cert-val (let [pem (header-cert->pem header-cert-val) certs (pem->certs pem) cert-count (count certs)] @@ -163,7 +146,7 @@ [ssl-client-cert] (if ssl-client-cert (let [cn (ssl-utils/get-cn-from-x509-certificate ssl-client-cert) - authenticated (not (empty? cn))] + authenticated (if (seq cn) true false)] (log/debug (i18n/trs "CN ''{0}'' provided by SSL certificate. Authenticated: {1}." cn authenticated)) {:client-cert-cn cn @@ -188,7 +171,7 @@ * config - Map of configuration data * request - Ring request containing client data" [config request] - (if-let [authorization (:authorization request)] + (if (:authorization request) {:client-cert (ring-auth/authorized-certificate request) :client-cert-cn (ring-auth/authorized-name request) :authenticated (true? (ring-auth/authorized-authenticated request))} @@ -242,8 +225,8 @@ (client-auth-info config request))) (defn make-request-mutable - [request] "Make the request mutable. This is required by the ruby layer." + [request] (HashMap. request)) (defn with-code-id @@ -268,7 +251,7 @@ wrap-params-for-jruby (with-code-id current-code-id) (as-jruby-request config) - clojure.walk/stringify-keys + walk/stringify-keys make-request-mutable (.handleRequest (:jruby-instance request)) response->map))) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/versioned_code_service/versioned_code_core.clj puppetserver-8.4.0/src/clj/puppetlabs/services/versioned_code_service/versioned_code_core.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/versioned_code_service/versioned_code_core.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/versioned_code_service/versioned_code_core.clj 2024-01-15 23:29:55.000000000 +0000 @@ -101,9 +101,9 @@ (throw-execution-error! e))))) (schema/defn ^:always-validate validate-config! - [{:keys [code-id-command code-content-command]} :- (schema/maybe VersionedCodeServiceConfig)] "Validates the versioned-code-service config. The config is considered valid if it is either empty or fully populated." + [{:keys [code-id-command code-content-command]} :- (schema/maybe VersionedCodeServiceConfig)] (when (or (and code-id-command (not code-content-command)) (and (not code-id-command) code-content-command)) diff -Nru puppetserver-7.9.5/src/clj/puppetlabs/services/versioned_code_service/versioned_code_service.clj puppetserver-8.4.0/src/clj/puppetlabs/services/versioned_code_service/versioned_code_service.clj --- puppetserver-7.9.5/src/clj/puppetlabs/services/versioned_code_service/versioned_code_service.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/clj/puppetlabs/services/versioned_code_service/versioned_code_service.clj 2024-01-15 23:29:55.000000000 +0000 @@ -14,9 +14,9 @@ (init [this context] - (if (nil? (get-in-config [:versioned-code :code-id-command])) + (when (nil? (get-in-config [:versioned-code :code-id-command])) (log/info (i18n/trs "No code-id-command set for versioned-code-service. Code-id will be nil."))) - (if (nil? (get-in-config [:versioned-code :code-content-command])) + (when (nil? (get-in-config [:versioned-code :code-content-command])) (log/info (i18n/trs "No code-content-command set for versioned-code-service. Attempting to fetch code content will fail."))) (vc-core/validate-config! (get-in-config [:versioned-code])) context) diff -Nru puppetserver-7.9.5/src/ruby/puppetserver-lib/puppet/server/ast_compiler.rb puppetserver-8.4.0/src/ruby/puppetserver-lib/puppet/server/ast_compiler.rb --- puppetserver-7.9.5/src/ruby/puppetserver-lib/puppet/server/ast_compiler.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/ruby/puppetserver-lib/puppet/server/ast_compiler.rb 2024-01-15 23:29:55.000000000 +0000 @@ -136,7 +136,7 @@ } # Use the existing environment with the requested name - Puppet::Pal.in_environment(compile_options['environment'], env_conf) do |pal| + Puppet::Pal.in_environment(compile_options['environment'], **env_conf) do |pal| # TODO: Given we hide this from plan authors this current iteration has only # the "required" data for now. Once we can get https://github.com/puppetlabs/bolt/pull/1770 # merged and promoted we can just use an empty hash. diff -Nru puppetserver-7.9.5/src/ruby/puppetserver-lib/puppet/server/master.rb puppetserver-8.4.0/src/ruby/puppetserver-lib/puppet/server/master.rb --- puppetserver-7.9.5/src/ruby/puppetserver-lib/puppet/server/master.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/ruby/puppetserver-lib/puppet/server/master.rb 2024-01-15 23:29:55.000000000 +0000 @@ -162,10 +162,6 @@ def terminate Puppet::Server::Config.terminate_puppet_server - # executor shutdown can be removed after puppetserver upgrades to a version of jruby - # following merge of https://github.com/jruby/jruby/pull/6127 - executor = Timeout.to_java.getInternalVariable('__executor__') - executor.shutdown end # @return [Array, nil] an array of hashes describing tasks @@ -283,7 +279,7 @@ [] when File.directory?(env.manifest) Dir.glob(File.join(env.manifest, '**/*.pp')) - when File.exists?(env.manifest) + when File.exist?(env.manifest) [env.manifest] else [] diff -Nru puppetserver-7.9.5/src/ruby/puppetserver-lib/puppet/server/network/http/handler.rb puppetserver-8.4.0/src/ruby/puppetserver-lib/puppet/server/network/http/handler.rb --- puppetserver-7.9.5/src/ruby/puppetserver-lib/puppet/server/network/http/handler.rb 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/src/ruby/puppetserver-lib/puppet/server/network/http/handler.rb 2024-01-15 23:29:55.000000000 +0000 @@ -36,7 +36,25 @@ def body(request) body = request["body"] if body.kind_of?(InputStream) - body.to_io.read() + io = body.to_io + content_type = '' + has_headers = request.key?('headers') + if has_headers && request['headers'].key?('content-type') + content_type = request['headers']['content-type'] + end + + case content_type + when nil, "", "application/octet-stream", "application/x-msgpack" + io.binmode + else + encoding = 'UTF-8' + if has_headers + encoding = request['headers'].fetch('content-encoding', 'UTF-8') + end + io.set_encoding(encoding) + end + + io.read else body end diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/general_puppet/general_puppet_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/general_puppet/general_puppet_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/general_puppet/general_puppet_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/general_puppet/general_puppet_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,9 +1,8 @@ (ns puppetlabs.general-puppet.general-puppet-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [me.raynes.fs :as fs] - [cheshire.core :as json] [puppetlabs.http.client.sync :as http-client] [puppetlabs.puppetserver.testutils :as testutils] [puppetlabs.trapperkeeper.testutils.logging :as logging] @@ -208,7 +207,7 @@ (let [response (testutils/get-static-file-content "dist/foo/files/bar.txt?code_id=foobar&environment=test")] (is (= 200 (:status response))) (is (= "test foobar dist/foo/files/bar.txt\n" (:body response))))) - (testing "the /static_file_content endpoint validates environment" + (testing "the /static_file_content endpoint validates environment" (doseq [[env-encoded env-decoded] [["hi%23cat" "hi#cat"] ["hi%20cat" "hi cat"] @@ -222,7 +221,7 @@ (is (= (ps-common/environment-validation-error-msg env-decoded) (:body response)))))) - (testing "the /static_file_content endpoint validates code-id" + (testing "the /static_file_content endpoint validates code-id" (doseq [[code-id-encoded code-id-decoded] [["hi%23cat" "hi#cat"] ["hi%20cat" "hi cat"] @@ -238,7 +237,7 @@ - (let [error-message "Error: A /static_file_content request requires an environment, a code-id, and a file-path"] + (let [error-message "Error: A /static_file_content request requires an environment, a code-id, and a file-path"] (testing "the /static_file_content endpoint returns an error if code_id is not provided" (let [response (testutils/get-static-file-content "modules/foo/files/bar.txt?environment=test")] (is (= 400 (:status response))) @@ -308,7 +307,7 @@ {:jruby-puppet {:gem-path gem-path}} (let [catalog (testutils/get-catalog)] - (is (= "{pp_auth_doodad => true, sf => Burning Finger, short => 22}" + (is (= "{pp_cli_auth => true, sf => Burning Finger, short => 22}" (get-in (first (filter #(= (get % "title") "trusted_hash") (get catalog "resources"))) ["parameters" "message"]))))))) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/auth_conf_test.clj puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/auth_conf_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/auth_conf_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/auth_conf_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.puppetserver.auth-conf-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [clojure.string :as str] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.http.client.sync :as http-client] @@ -12,7 +12,6 @@ [puppetlabs.puppetserver.testutils :as testutils :refer [http-get]] [me.raynes.fs :as fs] [ring.util.codec :as ring-codec] - [puppetlabs.trapperkeeper.testutils.logging :as logging] [cheshire.core :as cheshire]) (:import (java.io StringWriter))) @@ -32,8 +31,7 @@ (testutils/with-puppet-conf (fs/file test-resources-dir "puppet.conf"))) (deftest ^:integration request-with-ssl-cert-handled-via-tk-auth - (testing (str "Request with SSL certificate via trapperkeeper-authorization " - "handled") + (testing "Request with SSL certificate via trapperkeeper-authorization handled" (logutils/with-test-logging (bootstrap/with-puppetserver-running app @@ -114,8 +112,7 @@ (ks/pprint-to-string response))))))))) (deftest ^:integration request-with-x-client-headers-handled-via-tk-auth - (testing (str "Request with X-Client headers via trapperkeeper-authorization " - "handled") + (testing "Request with X-Client headers via trapperkeeper-authorization handled" (let [extension-value "UUUU-IIIII-DDD" cert (:cert (ssl-simple/gen-self-signed-cert "ssl-client" @@ -186,7 +183,7 @@ (deftest ^:integration custom-oids-passed-to-tk-auth (testing "puppet server successfully utilizes custom oid mappings and puppet short names for authorization" - (logging/with-test-logging + (logutils/with-test-logging (bootstrap/with-puppetserver-running-with-mock-jruby-puppet-fn "JRuby mocking is safe here because these tests are strictly validating the Clojure tk-auth checks." diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/bootstrap_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/bootstrap_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/bootstrap_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/bootstrap_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,6 @@ (ns puppetlabs.puppetserver.bootstrap-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] + [puppetlabs.kitchensink.file :as ks-file] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.trapperkeeper.testutils.logging :as logging] [puppetlabs.puppetserver.certificate-authority :as ca] @@ -17,10 +18,10 @@ (testing "Private keys have the correct permissions." (let [pk-dir (str bootstrap/server-conf-dir "/ssl/private_keys") pks (fs/find-files pk-dir #".*pem$")] - (is (= ca/private-key-dir-perms (ca/get-file-perms pk-dir))) + (is (= ca/private-key-dir-perms (ks-file/get-perms pk-dir))) (is (= ca/private-key-perms - (ca/get-file-perms (str bootstrap/server-conf-dir + (ks-file/get-perms (str bootstrap/server-conf-dir "/ca/ca_key.pem")))) (doseq [pk pks] - (is (= ca/private-key-perms (ca/get-file-perms (.getPath pk))))))) + (is (= ca/private-key-perms (ks-file/get-perms (.getPath pk))))))) (is (true? true))))) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/bootstrap_testutils.clj puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/bootstrap_testutils.clj --- puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/bootstrap_testutils.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/bootstrap_testutils.clj 2024-01-15 23:29:55.000000000 +0000 @@ -6,7 +6,6 @@ [me.raynes.fs :as fs] [puppetlabs.ssl-utils.core :as ssl-utils] [puppetlabs.ssl-utils.simple :as ssl-simple] - [puppetlabs.ssl-utils.core :as utils] [schema.core :as schema] [puppetlabs.puppetserver.certificate-authority :as ca] [puppetlabs.services.jruby.jruby-puppet-testutils @@ -86,7 +85,7 @@ any important test coverage. For this reason, we require a `docstring` argument to be passed in, as a sort of annotation explaining why you feel it's safe to use this mocking in your test." - [docstring app services config-overrides & body] + [_docstring app services config-overrides & body] (let [config (load-dev-config-with-overrides config-overrides)] `(let [services# (conj ~services (jruby-puppet-testutils/mock-jruby-pool-manager-service @@ -103,7 +102,7 @@ any important test coverage. For this reason, we require a `docstring` argument to be passed in, as a sort of annotation explaining why you feel it's safe to use this mocking in your test." - [docstring app services config-overrides mock-jruby-puppet-fn & body] + [_docstring app services config-overrides mock-jruby-puppet-fn & body] (let [config (load-dev-config-with-overrides config-overrides)] `(let [services# (conj ~services (jruby-puppet-testutils/mock-jruby-pool-manager-service @@ -140,7 +139,7 @@ any important test coverage. For this reason, we require a `docstring` argument to be passed in, as a sort of annotation explaining why you feel it's safe to use this mocking in your test." - [docstring app config-overrides & body] + [_docstring app config-overrides & body] (let [config (load-dev-config-with-overrides config-overrides)] `(let [services# (services-from-dev-bootstrap-plus-mock-jruby-pool-manager-service @@ -157,7 +156,7 @@ any important test coverage. For this reason, we require a `docstring` argument to be passed in, as a sort of annotation explaining why you feel it's safe to use this mocking in your test." - [docstring app config-overrides mock-jruby-puppet-fn & body] + [_docstring app config-overrides mock-jruby-puppet-fn & body] (let [config (load-dev-config-with-overrides config-overrides)] `(let [services# (services-from-dev-bootstrap-plus-mock-jruby-pool-manager-service @@ -171,7 +170,7 @@ (defn write-to-stream [o] (let [s (ByteArrayOutputStream.)] - (utils/obj->pem! o s) + (ssl-utils/obj->pem! o s) (-> s .toByteArray ByteArrayInputStream.))) (schema/defn get-ca-cert-for-running-server :- ca/Certificate diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/error_handling_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/error_handling_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/puppetserver/error_handling_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/puppetserver/error_handling_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,7 +1,7 @@ (ns puppetlabs.puppetserver.error-handling-int-test (:require - [clojure.test :refer :all] - [puppetlabs.trapperkeeper.testutils.logging :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] + [puppetlabs.trapperkeeper.testutils.logging :refer [with-test-logging]] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.puppetserver.testutils :as testutils] [puppetlabs.puppetserver.certificate-authority :as ca] diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/analytics/analytics_service_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/analytics/analytics_service_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/analytics/analytics_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/analytics/analytics_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,6 @@ (ns puppetlabs.services.analytics.analytics-service-test (:require - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing]] [puppetlabs.dujour.version-check :as version-check] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap-testutils] [puppetlabs.trapperkeeper.testutils.logging :as logutils])) @@ -16,7 +16,7 @@ {:request-values request-values :update-server-url update-server-url}))] (with-redefs - [version-check/check-for-updates! version-check-test-fn] + [version-check/check-for-update version-check-test-fn] (logutils/with-test-logging (bootstrap-testutils/with-puppetserver-running-with-mock-jrubies "Mocking is safe here because we're not doing anything with JRubies, just making sure diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/certificate_authority/certificate_authority_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/certificate_authority/certificate_authority_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/certificate_authority/certificate_authority_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/certificate_authority/certificate_authority_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,25 +1,32 @@ (ns puppetlabs.services.certificate-authority.certificate-authority-int-test (:require - [clojure.test :refer :all] - [puppetlabs.kitchensink.core :as ks] - [puppetlabs.ssl-utils.core :as utils] - [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] - [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] - [puppetlabs.puppetserver.testutils :as testutils :refer - [ca-cert localhost-cert localhost-key ssl-request-options http-get]] - [puppetlabs.trapperkeeper.testutils.logging :as logutils] - [schema.test :as schema-test] - [me.raynes.fs :as fs] - [cheshire.core :as json] - [puppetlabs.http.client.sync :as http-client] - [puppetlabs.ssl-utils.core :as ssl-utils] - [puppetlabs.puppetserver.certificate-authority :as ca] - [puppetlabs.services.ca.certificate-authority-core :refer :all] - [ring.mock.request :as mock] - [me.raynes.fs :as fs] - [clj-time.format :as time-format] - [clj-time.core :as time]) - (:import (javax.net.ssl SSLException))) + [cheshire.core :as json] + [clj-time.core :as time] + [clj-time.format :as time-format] + [clojure.java.io :as io] + [clojure.test :refer [deftest is testing use-fixtures]] + [me.raynes.fs :as fs] + [puppetlabs.http.client.sync :as http-client] + [puppetlabs.kitchensink.core :as ks] + [puppetlabs.kitchensink.file :as ks-file] + [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] + [puppetlabs.puppetserver.certificate-authority :as ca] + [puppetlabs.puppetserver.testutils :as testutils :refer [http-get]] + [puppetlabs.rbac-client.protocols.activity :as act-proto] + [puppetlabs.rbac-client.testutils.dummy-rbac-service :refer [dummy-rbac-service]] + [puppetlabs.services.ca.ca-testutils :as ca-test-utils] + [puppetlabs.services.ca.certificate-authority-core :refer [handle-get-certificate-revocation-list]] + [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] + [puppetlabs.ssl-utils.core :as ssl-utils] + [puppetlabs.ssl-utils.simple :as simple] + [puppetlabs.trapperkeeper.services :as tk-services] + [puppetlabs.trapperkeeper.testutils.logging :as logutils] + [ring.mock.request :as mock] + [ring.util.codec :as ring-codec] + [schema.test :as schema-test]) + (:import (java.util Date Random) + (java.util.concurrent TimeUnit) + (javax.net.ssl SSLException))) (def test-resources-dir "./dev-resources/puppetlabs/services/certificate_authority/certificate_authority_int_test") @@ -44,10 +51,219 @@ ([body] (merge (cert-status-request-params) {:body body}))) +(defn create-ca-cert + [name serial] + (let [keypair (ssl-utils/generate-key-pair) + public-key (ssl-utils/get-public-key keypair) + private-key (ssl-utils/get-private-key keypair) + x500-name (ssl-utils/cn name) + validity (ca/cert-validity-dates 3600) + ca-exts (ca/create-ca-extensions public-key public-key)] + {:public-key public-key + :private-key private-key + :x500-name x500-name + :certname name + :cert (ssl-utils/sign-certificate + x500-name + private-key + serial + (:not-before validity) + (:not-after validity) + x500-name + public-key + ca-exts)})) + +(defn generate-a-csr + [certname extensions attributes] + (let [key-pair (ssl-utils/generate-key-pair) + csr (ssl-utils/generate-certificate-request + key-pair + (ssl-utils/cn certname) + extensions + attributes) + csr-path (str bootstrap/server-conf-dir "/ca/requests/" certname ".pem")] + (ssl-utils/obj->pem! csr csr-path) + key-pair)) + +(defn delete-all-csrs + [] + (let [csr-path (str bootstrap/server-conf-dir "/ca/requests/")] + (ks-file/delete-recursively csr-path))) + +(defn generate-and-sign-a-cert! + [certname] + (let [cert-path (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + key-path (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + ca-cert-path (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + status-url (str "https://localhost:8140/puppet-ca/v1/certificate_status/" certname) + cert-endpoint (str "https://localhost:8140/puppet-ca/v1/certificate/" certname) + request-opts {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path} + key-pair (generate-a-csr certname [] [])] + (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json"}})) + (let [cert-request (http-client/get cert-endpoint {:ssl-ca-cert ca-cert-path}) + private-key-file (ks/temp-file) + public-key-file (ks/temp-file)] + (ssl-utils/key->pem! (ssl-utils/get-public-key key-pair) public-key-file) + (ssl-utils/key->pem! (ssl-utils/get-private-key key-pair) private-key-file) + {:signed-cert (slurp (:body cert-request)) + :public-key public-key-file + :private-key private-key-file}))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Tests +(deftest reporting-activity-handles-errors + (let [cert-path (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + key-path (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + ca-cert-path (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem") + test-service (tk-services/service + act-proto/ActivityReportingService + [] + (report-activity! [_this body] + (throw (Exception. "Foo"))))] + (testing "returns expiration dates for all CA certs and CRLs" + (testutils/with-stub-puppet-conf + (bootstrap/with-puppetserver-running-with-services + app + (concat (bootstrap/services-from-dev-bootstrap) [test-service]) + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path + :ssl-crl-path crl-path}} + (let [certname "test_cert" + csr (ssl-utils/generate-certificate-request + (ssl-utils/generate-key-pair) + (ssl-utils/cn certname)) + csr-path (str bootstrap/server-conf-dir "/ca/requests/" certname ".pem") + signed-cert-path (str bootstrap/server-conf-dir "/ca/signed/" certname ".pem") + status-url (str "https://localhost:8140/puppet-ca/v1/certificate_status/" certname) + request-opts {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path}] + + (ssl-utils/obj->pem! csr csr-path) + (testing "Sign the waiting CSR" + (logutils/with-test-logging + (let [response (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json"}}))] + (is (= 204 (:status response))) + (is (logged? #"Reporting CA event failed with: Foo" :error)) + (is (logged? #"Payload.*commit" :error)) + (is (fs/exists? signed-cert-path)) + (is (logged? #"Entity localhost signed 1 certificate.*" :info))))) + + (testing "Revoke the cert" + (logutils/with-test-logging + (let [response (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"revoked\"}" + :headers {"content-type" "application/json" "X-Authentication" "test"}}))] + (is (= 204 (:status response))) + (is (logged? #"Reporting CA event failed with: Foo" :error)) + (is (logged? #"Payload.*commit" :error)) + (is (logged? #"Entity localhost revoked 1 certificate.*" :info))))))))))) + +(deftest new-cert-signing-respects-agent-renewal-support-indication + (testutils/with-config-dirs + {(str test-resources-dir "/infracrl_test/master/conf/ssl") (str bootstrap/server-conf-dir "/ssl") + (str test-resources-dir "/infracrl_test/master/conf/ca") (str bootstrap/server-conf-dir "/ca")} + (let [cert-path (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + key-path (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + ca-cert-path (str bootstrap/server-conf-dir "/ca/ca_crt.pem")] + (testing "with a puppetserver configured for auto-renewal" + (testutils/with-stub-puppet-conf + (bootstrap/with-puppetserver-running + app + {:certificate-authority { :allow-auto-renewal true} + :jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")}} + (testing "signs a cert with a short ttl when the capability indicator is present" + (let [certname (ks/rand-str :alpha-lower 8) + csr (ssl-utils/generate-certificate-request + (ssl-utils/generate-key-pair) + (ssl-utils/cn certname) + [] + [{:oid "1.3.6.1.4.1.34380.1.3.2" :value true}]) + csr-path (ks/temp-file "test_csr.pem") + signed-cert-path (str bootstrap/server-conf-dir "/ca/signed/" certname ".pem") + status-url (str "https://localhost:8140/puppet-ca/v1/certificate_status/" certname) + url (str "https://localhost:8140/puppet-ca/v1/certificate_request/" certname) + request-opts {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path}] + + (ssl-utils/obj->pem! csr csr-path) + (testing "submit a CSR via the API" + (let [response (http-client/put + url + (merge request-opts {:body (slurp csr-path) + :headers {"x-puppet-version" "8.2.0"}}))] + (is (= 200 (:status response))))) + (testing "Sign the waiting CSR" + (let [response (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json"}}))] + (is (= 204 (:status response))) + (is (fs/exists? signed-cert-path)) + (let [signed-cert (ssl-utils/pem->cert signed-cert-path)] + (testing "new not-after should be 89 days (and some fraction) away" + (let [diff (- (.getTime (.getNotAfter signed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 89 days))))))))) + (testing "signs a cert with a long ttl when the capability indicator is not present" + (let [certname (ks/rand-str :alpha-lower 8) + csr (ssl-utils/generate-certificate-request + (ssl-utils/generate-key-pair) + (ssl-utils/cn certname)) + csr-path (ks/temp-file "test_csr.pem") + signed-cert-path (str bootstrap/server-conf-dir "/ca/signed/" certname ".pem") + status-url (str "https://localhost:8140/puppet-ca/v1/certificate_status/" certname) + url (str "https://localhost:8140/puppet-ca/v1/certificate_request/" certname) + request-opts {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path}] + + (ssl-utils/obj->pem! csr csr-path) + (testing "submit a CSR via the API" + (let [response (http-client/put + url + (merge request-opts {:body (slurp csr-path) + :headers {"x-puppet-version" "7.1.8"}}))] + (is (= 200 (:status response))))) + (testing "Sign the waiting CSR" + (let [response (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json"}}))] + (is (= 204 (:status response))) + (is (fs/exists? signed-cert-path)) + (let [signed-cert (ssl-utils/pem->cert signed-cert-path)] + (testing "new not-after should be 5 years (and some fraction) away" + (let [diff (- (.getTime (.getNotAfter signed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= (- (* 365 5) 1) days))))))))))))))) (deftest ^:integration cert-on-whitelist-test (testing "requests made when cert is on whitelist" (logutils/with-test-logging @@ -187,8 +403,7 @@ (ks/pprint-to-string response)))))))))) (deftest ^:integration certificate-with-ca-true-extension-refused - (testing (str "Validates that the server rejects a csr for signing" - " that has the v3 CA:TRUE extension") + (testing "Validates that the server rejects a csr for signing that has the v3 CA:TRUE extension" (let [server-conf-dir (str test-resources-dir "/ca_true_test/master/conf") req-dir (str server-conf-dir "/ca/requests") key-pair (ssl-utils/generate-key-pair) @@ -219,8 +434,7 @@ (fs/delete-dir req-dir)))))) (deftest ^:integration double-encoded-request-not-allowed - (testing (str "client not able to unintentionally get access to CA endpoint " - "by double-encoding request uri") + (testing "client not able to unintentionally get access to CA endpoint by double-encoding request uri" ;; The following tests are intended to show that a client is not able ;; to unintentionally gain access to info for a different client by ;; double-encoding a character in the client name portion of the @@ -275,6 +489,7 @@ (deftest ^:integration crl-reloaded-without-server-restart (testutils/with-stub-puppet-conf + (with-redefs [act-proto/report-activity! (fn [_ _] nil)] (bootstrap/with-puppetserver-running app {:jruby-puppet @@ -287,7 +502,6 @@ (let [key-pair (ssl-utils/generate-key-pair) subject "crl_reload" subject-dn (ssl-utils/cn subject) - public-key (ssl-utils/get-public-key key-pair) private-key (ssl-utils/get-private-key key-pair) private-key-file (ks/temp-file) csr (ssl-utils/generate-certificate-request key-pair subject-dn) @@ -335,7 +549,7 @@ (let [ssl-exception-for-request? #(try (client-request) false - (catch SSLException e + (catch SSLException _ true))] (is (loop [times 30] (cond @@ -343,10 +557,11 @@ (zero? times) false :else (do (Thread/sleep 500) - (recur (dec times)))))))))))) + (recur (dec times))))))))))))) (deftest ^:integration revoke-compiler-test (testing "Compiler certificate revocation " + (with-redefs [act-proto/report-activity! (fn [_ _] nil)] (testutils/with-config-dirs {(str test-resources-dir "/infracrl_test/master/conf/ssl") (str bootstrap/server-conf-dir "/ssl") (str test-resources-dir "/infracrl_test/master/conf/ca") (str bootstrap/server-conf-dir "/ca")} @@ -364,8 +579,8 @@ {:certificate-authority {:enable-infra-crl true}} (testing "should update infrastructure CRL" (let [ca-cert' (ssl-utils/pem->ca-cert ca-cert ca-key) - cm-cert (utils/pem->cert (ca/path-to-cert signed-dir subject)) - node-cert (utils/pem->cert (ca/path-to-cert signed-dir node-subject)) + cm-cert (ssl-utils/pem->cert (ca/path-to-cert signed-dir subject)) + node-cert (ssl-utils/pem->cert (ca/path-to-cert signed-dir node-subject)) options {:ssl-cert ca-cert :ssl-key ca-key :ssl-ca-cert ca-cert @@ -386,24 +601,27 @@ (let [revoke-response (cert-status-request "revoked" subject)] ;; If the revocation was successful infra CRL should contain above revoked compiler's cert (is (= 204 (:status revoke-response))) - (is (utils/revoked? (utils/pem->ca-crl infra-crl ca-cert') cm-cert)))) + (is (ssl-utils/revoked? (ssl-utils/pem->ca-crl infra-crl ca-cert') cm-cert)))) (testing "Infra CRL should NOT contain a revoked non-compiler's certificate" (let [revoke-response (cert-status-request "revoked" node-subject)] (is (= 204 (:status revoke-response))) - (is (not (utils/revoked? (utils/pem->ca-crl infra-crl ca-cert') node-cert))))))) + (is (not (ssl-utils/revoked? (ssl-utils/pem->ca-crl infra-crl ca-cert') node-cert))))))) (testing "Verify correct CRL is returned depending on enable-infra-crl" (let [request (mock/request :get "/v1/certificate_revocation_list/mynode") + ca-settings (ca-test-utils/ca-settings "") infra-crl-response (handle-get-certificate-revocation-list - request {:cacrl ca-crl - :infra-crl-path infra-crl - :enable-infra-crl true}) + request (assoc ca-settings + :cacrl ca-crl + :infra-crl-path infra-crl + :enable-infra-crl true)) infra-crl-response-body (:body infra-crl-response) full-crl-response (handle-get-certificate-revocation-list - request {:cacrl ca-crl - :infra-crl-path infra-crl - :enable-infra-crl false}) + request (assoc ca-settings + :cacrl ca-crl + :infra-crl-path infra-crl + :enable-infra-crl false)) full-crl-response-body (:body full-crl-response)] (is (map? infra-crl-response)) (is (= 200 (:status infra-crl-response))) @@ -449,9 +667,10 @@ (is (map? crl-response)) (is (= 200 (:status crl-response))) (is (string? crl-response-body)) - (is (= crl-response-body (slurp ca-crl)))))))))) + (is (= crl-response-body (slurp ca-crl))))))))))) (deftest ^:integration clean-infrastructure-certs + (with-redefs [act-proto/report-activity! (fn [_ _] nil)] (testutils/with-config-dirs {(str test-resources-dir "/infracrl_test/master/conf/ssl") (str bootstrap/server-conf-dir "/ssl") (str test-resources-dir "/infracrl_test/master/conf/ca") (str bootstrap/server-conf-dir "/ca")} @@ -468,9 +687,9 @@ {:certificate-authority {:enable-infra-crl true}} (let [subject1 "compile-master" cert1-path (ca/path-to-cert (str bootstrap/server-conf-dir "/ca/signed") subject1) - cert1 (utils/pem->cert cert1-path) + cert1 (ssl-utils/pem->cert cert1-path) cert2-path (ca/path-to-cert (str bootstrap/server-conf-dir "/ca/signed") subject2) - cert2 (utils/pem->cert cert2-path)] + cert2 (ssl-utils/pem->cert cert2-path)] (testing "should update infrastructure CRL with multiple certs" (let [ca-cert (ssl-utils/pem->ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") (str bootstrap/server-conf-dir "/ca/ca_key.pem")) @@ -488,21 +707,21 @@ "application/json"}}))] ;; If the revocation was successful infra CRL should contain above revoked compiler's cert (is (= 200 (:status revoke-response))) - (is (utils/revoked? (utils/pem->ca-crl - (str bootstrap/server-conf-dir "/ca/infra_crl.pem") - ca-cert) + (is (ssl-utils/revoked? (ssl-utils/pem->ca-crl + (str bootstrap/server-conf-dir "/ca/infra_crl.pem") + ca-cert) cert1)) - (is (utils/revoked? (utils/pem->ca-crl + (is (ssl-utils/revoked? (ssl-utils/pem->ca-crl (str bootstrap/server-conf-dir "/ca/infra_crl.pem") ca-cert) cert2)) (is (false? (fs/exists? cert1-path))) - (is (false? (fs/exists? cert2-path)))))))))))) + (is (false? (fs/exists? cert2-path))))))))))))) (deftest ^:integration certificate-status-returns-auth-ext-info - (testing (str "Validates that the certificate_status endpoint" - "includes authorization extensions for certs and CSRs") + (testing "Validates that the certificate_status endpoint includes authorization extensions for certs and CSRs" + (with-redefs [act-proto/report-activity! (fn [_ _] nil)] (let [request-dir (str bootstrap/server-conf-dir "/ca/requests") key-pair (ssl-utils/generate-key-pair) subjectDN (ssl-utils/cn "test_cert_with_auth_ext") @@ -555,7 +774,66 @@ (let [status-body (json/parse-string (:body status-response))] (is (= auth-exts (get status-body "authorization_extensions"))) (is (= "signed" (get status-body "state")))))))) - (fs/delete (str bootstrap/server-conf-dir "/ca/signed/test_cert_with_auth_ext.pem")))) + (fs/delete (str bootstrap/server-conf-dir "/ca/signed/test_cert_with_auth_ext.pem"))))) + +(defn generate-inventory-entry + [^Random random-generator number] + (format "0x%H 2023-09-20T16:28:17UTC 2028-09-19T16:28:17UTC host-%d-name.thing.to.take.up.spaces\n" (abs (.nextInt random-generator)) number)) + +(deftest ^:integration certificate-inventory-file-management + (testing "Validates that the certificate_status endpoint includes authorization extensions for certs and CSRs" + (with-redefs [act-proto/report-activity! (fn [_ _] nil)] + (let [inventory-path (str bootstrap/server-conf-dir "/ca/inventory.txt") + inventory-content (if (fs/exists? inventory-path) + (slurp inventory-path) + "") + request-dir (str bootstrap/server-conf-dir "/ca/requests") + key-pair (ssl-utils/generate-key-pair) + subjectDN (ssl-utils/cn "test_cert_with_auth_ext") + auth-ext-short-name {:oid (:pp_auth_role ca/puppet-short-names) + :critical false + :value "true"} + auth-ext-oid {:oid "1.3.6.1.4.1.34380.1.3.1.2" + :critical false + :value "true"} + csr (ssl-utils/generate-certificate-request key-pair + subjectDN + [auth-ext-short-name + auth-ext-oid]) + random-generator (Random.)] + (fs/mkdirs request-dir) + (ssl-utils/obj->pem! csr (str request-dir "/test_cert_with_auth_ext.pem")) + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority {:allow-authorization-extensions true}} + (testing "Adding to a very large inventory file works correctly" + (spit inventory-path inventory-content) + ;; internally the inventory append uses a 64K buffer, so make sure it is larger than that. + (loop [hostnames (map (partial generate-inventory-entry random-generator) (range 0 10000)) + hostname (first hostnames)] + (when hostname + (spit inventory-path hostname :append true) + (recur (rest hostnames) + (second hostnames)))) + + (let [sign-response (http-client/put + (str "https://localhost:8140/" + "puppet-ca/v1/certificate_status/test_cert_with_auth_ext") + (cert-status-request-params "{\"desired_state\": \"signed\"}"))] + (is (= 204 (:status sign-response))) + ;; inventory file should have "test_cert_with_auth_ext" in it + (let [new-inventory-contents (slurp inventory-path)] + (is (re-find #"test_cert_with_auth_ext" new-inventory-contents))))))) + (fs/delete (str bootstrap/server-conf-dir "/ca/signed/test_cert_with_auth_ext.pem"))))) (deftest csr-api-test (testutils/with-stub-puppet-conf @@ -603,6 +881,230 @@ (is (not (fs/exists? saved-csr))))) (fs/delete csr-file))))) +(deftest csr-api-puppet-version-test + (testutils/with-stub-puppet-conf + (bootstrap/with-puppetserver-running-with-config + app + (bootstrap/load-dev-config-with-overrides + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")}}) + (let [request-dir (str bootstrap/server-conf-dir "/ca/requests") + key-pair (ssl-utils/generate-key-pair) + subjectDN (ssl-utils/cn "test_version_cert") + csr (ssl-utils/generate-certificate-request key-pair subjectDN) + csr-file (ks/temp-file "test_version_csr.pem") + saved-csr (str request-dir "/test_version_cert.pem") + url "https://localhost:8140/puppet-ca/v1/certificate_request/test_version_cert" + request-opts {:ssl-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text + :headers {"content-type" "text/plain" + "x-puppet-version" "8.2.0"}}] + (ssl-utils/obj->pem! csr csr-file) + (testing "submit a CSR via the API" + (let [response (http-client/put + url + (merge request-opts {:body (slurp csr-file)}))] + (is (= 200 (:status response))) + (fs/delete csr-file))) + (testing "delete a CSR via the API" + (let [response (http-client/delete + url + request-opts)] + (is (= 204 (:status response))) + (is (not (fs/exists? saved-csr))))))))) + +(deftest csr-activity-service-cert + (let [reported-activity (atom []) + test-service (tk-services/service act-proto/ActivityReportingService + [] + (report-activity! [_this body] + (swap! reported-activity conj body))) + cert-path (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + key-path (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + ca-cert-path (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")] + + (testutils/with-stub-puppet-conf + (bootstrap/with-puppetserver-running-with-services + app + (concat (bootstrap/services-from-dev-bootstrap) [test-service]) + (bootstrap/load-dev-config-with-overrides + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path + :ssl-crl-path crl-path}}) + (let [certname "test_cert" + csr (ssl-utils/generate-certificate-request + (ssl-utils/generate-key-pair) + (ssl-utils/cn certname)) + csr-path (str bootstrap/server-conf-dir "/ca/requests/" certname ".pem") + status-url (str "https://localhost:8140/puppet-ca/v1/certificate_status/" certname) + request-opts {:ssl-cert cert-path + :ssl-key key-path + :ssl-ca-cert ca-cert-path} + requester-name (-> (ssl-utils/pem->ca-cert cert-path key-path) + (ssl-utils/get-subject-from-x509-certificate) + (ssl-utils/x500-name->CN))] + + + (ssl-utils/obj->pem! csr csr-path) + + (testing "Sign the waiting CSR" + (let [response (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json"}})) + activity-events (get-in (first @reported-activity) [:commit :events]) + msg-matcher (re-pattern (str "Entity " requester-name " signed 1 certificate: " certname))] + (is (= 204 (:status response))) + (is (re-find msg-matcher (:message (first activity-events)))) + (is (= 1 (count @reported-activity))))) + + (testing "Revoke the cert" + (let [response (http-client/put + status-url + (merge request-opts + {:body "{\"desired_state\": \"revoked\"}" + :headers {"content-type" "application/json"}})) + activity-events (get-in (second @reported-activity) [:commit :events]) + msg-matcher (re-pattern (str "Entity " requester-name " revoked 1 certificate: " certname))] + (is (= 204 (:status response))) + (is (re-find msg-matcher (:message (first activity-events)))) + (is (= 2 (count @reported-activity))))) + + (fs/delete csr-path)))))) + +(deftest csr-activity-service-token + (testutils/with-config-dirs + {(str test-resources-dir "/infracrl_test/master/conf/ssl") (str bootstrap/server-conf-dir "/ssl") + (str test-resources-dir "/infracrl_test/master/conf/ca") (str bootstrap/server-conf-dir "/ca")} + (let [reported-activity (atom []) + test-service (tk-services/service + act-proto/ActivityReportingService + [] + (report-activity! [_this body] + (swap! reported-activity conj body)))] + + (testutils/with-stub-puppet-conf + (bootstrap/with-puppetserver-running-with-services + app + (concat (bootstrap/services-from-dev-bootstrap) [test-service dummy-rbac-service]) + (bootstrap/load-dev-config-with-overrides + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :authorization {:version 1 + :rules [{:match-request + {:path "/" + :type "path"} + :allow [{:rbac {:permission "cert_requests:accept_reject:*"}}] + :sort-order 1 + :name "cert"}]}}) + (let [request-dir (str bootstrap/server-conf-dir "/ca/requests") + key-pair (ssl-utils/generate-key-pair) + certname "test_cert" + subjectDN (ssl-utils/cn certname) + csr (ssl-utils/generate-certificate-request key-pair subjectDN) + saved-csr (str request-dir "/test_cert.pem") + status-url (str "https://localhost:8140/puppet-ca/v1/certificate_status/" certname) + request-opts {:ssl-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem")}] + (ssl-utils/obj->pem! csr saved-csr) + + (testing "Sign the waiting CSR" + (let [response (http-client/put + status-url + (merge request-opts {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json" "X-Authentication" "test"}})) + activity-events (get-in (first @reported-activity) [:commit :events]) + msg-matcher (re-pattern (str "Entity test_user signed 1 certificate: " certname))] + (is (= 204 (:status response))) + (is (re-find msg-matcher (:message (first activity-events)))) + (is (= 1 (count @reported-activity))))) + + (testing "Revoke the cert" + (let [response (http-client/put status-url + (merge request-opts {:body "{\"desired_state\": \"revoked\"}" + :headers {"content-type" "application/json" "X-Authentication" "test"}})) + activity-events (get-in (second @reported-activity) [:commit :events]) + msg-matcher (re-pattern (str "Entity test_user revoked 1 certificate: " certname))] + + (is (= 204 (:status response))) + (is (re-find msg-matcher (:message (first activity-events)))) + (is (= 2 (count @reported-activity))))) + + (fs/delete saved-csr))))))) + +(deftest csr-activity-service-default + + (let [reported-activity (atom []) + test-service (tk-services/service + act-proto/ActivityReportingService + [] + (report-activity! [_this body] + (swap! reported-activity conj body)))] + + (testutils/with-stub-puppet-conf + (bootstrap/with-puppetserver-running-with-services + app + (concat (bootstrap/services-from-dev-bootstrap) [test-service]) + + (update-in (bootstrap/load-dev-config-with-overrides + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver {:host "0.0.0.0" + :port 8140}}) [:webserver] dissoc :ssl-host :ssl-port) + + (let [request-dir (str bootstrap/server-conf-dir "/ca/requests") + key-pair (ssl-utils/generate-key-pair) + certname "test_cert" + subjectDN (ssl-utils/cn certname) + csr (ssl-utils/generate-certificate-request key-pair subjectDN) + saved-csr (str request-dir "/test_cert.pem") + status-url (str "http://localhost:8140/puppet-ca/v1/certificate_status/" certname) + request-opts {}] + (ssl-utils/obj->pem! csr saved-csr) + + (testing "Sign the waiting CSR" + (let [response (http-client/put + status-url + (merge request-opts {:body "{\"desired_state\": \"signed\"}" + :headers {"content-type" "application/json"}})) + activity-events (get-in (first @reported-activity) [:commit :events]) + msg-matcher (re-pattern (str "Entity CA signed 1 certificate: " certname))] + (is (= 204 (:status response))) + (is (re-find msg-matcher (:message (first activity-events)))) + (is (= 1 (count @reported-activity))))) + + (testing "Revoke the cert" + (let [response (http-client/put + status-url + (merge request-opts {:body "{\"desired_state\": \"revoked\"}" + :headers {"content-type" "application/json"}})) + activity-events (get-in (second @reported-activity) [:commit :events]) + msg-matcher (re-pattern (str "Entity CA revoked 1 certificate: " certname))] + (is (= 204 (:status response))) + (is (re-find msg-matcher (:message (first activity-events)))) + (is (= 2 (count @reported-activity))))) + + (fs/delete saved-csr)))))) + (deftest ca-expirations-endpoint-test (testing "returns expiration dates for all CA certs and CRLs" (bootstrap/with-puppetserver-running-with-mock-jrubies @@ -661,3 +1163,669 @@ :as :text :body "Bad data"})] (is (= 400 (:status response))))))) + +(deftest ca-bulk-signing-endpoint-test + (testing "ca bulk signing endpoint " + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")}} + + (testing "returns 200 with valid payload" + ;; note- more extensive testing of the behavior is done with the testing in sign-multiple-certificate-signing-requests!-test + (let [certname (ks/rand-str :alpha-lower 16) + certname-no-exist (ks/rand-str :alpha-lower 16) + certname-with-bad-extension (ks/rand-str :alpha-lower 16) + _ (generate-a-csr certname [] []) + _ (generate-a-csr certname-with-bad-extension [{:oid "1.9.9.9.9.9.0" :value "true" :critical false}] []) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/sign" + {:body (json/encode {:certnames [certname certname-no-exist certname-with-bad-extension]}) + :ssl-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text + :headers {"Accept" "application/json"}})] + (is (= 200 (:status response))) + (is (= {:signed [certname] + :no-csr [certname-no-exist] + :signing-errors [certname-with-bad-extension]} + (json/parse-string (:body response) true))))) + (testing "throws schema violation for invalid certname" + (let [error-msg "{\"kind\":\"schema-violation\"" + response (http-client/post + "https://localhost:8140/puppet-ca/v1/sign" + {:body (json/encode {:certnames [1 2 3]}) + :ssl-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text + :headers {"Accept" "application/json"}}) + body (:body response)] + (is (= 422 (:status response))) + (is (.contains body error-msg))))))) + +(deftest ca-bulk-signing-all-endpoint-test + ;; ensure the csr directory is empty as other tests leave cruft behind + (delete-all-csrs) + (testing "returns 200 response" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")}} + (testing "returns 200 with valid payload" + ;; note- more extensive testing of the behavior is done with the testing in sign-multiple-certificate-signing-requests!-test + (let [certname (ks/rand-str :alpha-lower 16) + certname-with-bad-extension (ks/rand-str :alpha-lower 16) + _ (generate-a-csr certname [] []) + _ (generate-a-csr certname-with-bad-extension [{:oid "1.9.9.9.9.9.0" :value "true" :critical false}] []) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/sign/all" + {:ssl-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text + :headers {"Accept" "application/json"}})] + (is (= 200 (:status response))) + (is (= {:signed [certname] + ;; this would represent any files that are removed between when the set is collected, and when they are processed. + :no-csr [] + :signing-errors [certname-with-bad-extension]} + (json/parse-string (:body response) true)))))))) + +(deftest ca-certificate-renew-endpoint-test + (testing "with the feature enabled" + (testing "with allow-header-cert-info = false (default)" + (testing "returns a 200 OK response when feature is enabled, + a certificate is present in the request, and that cert matches + the signing cert" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) ;; ensure some time has passed so the timestamps are different + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:ssl-cert (str signed-cert-file) + :ssl-key (str (:private-key generated-cert-info)) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 200 (:status response))) + (let [renewed-cert-pem (:body response) + renewed-cert-file (ks/temp-file) + _ (spit renewed-cert-file renewed-cert-pem) + renewed-cert (ssl-utils/pem->cert renewed-cert-file) + signed-cert (ssl-utils/pem->cert signed-cert-file)] + (testing "serial number has been incremented" + (is (< (.getSerialNumber signed-cert) (.getSerialNumber renewed-cert)))) + (testing "not before time stamps have changed" + (is (true? (.before (.getNotBefore signed-cert) (.getNotBefore renewed-cert))))) + (testing "new not-after is earlier than before" + (is (true? (.after (.getNotAfter signed-cert) (.getNotAfter renewed-cert))))) + (testing "new not-after should be 89 days (and some fraction) away" + (let [diff (- (.getTime (.getNotAfter renewed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 89 days)))))))) + + (testing "Honors non-default auto-renewal-cert-ttl" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true + :auto-renewal-cert-ttl "42d"}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) ;; ensure some time has passed so the timestamps are different + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:ssl-cert (str signed-cert-file) + :ssl-key (str (:private-key generated-cert-info)) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 200 (:status response))) + (let [renewed-cert-pem (:body response) + renewed-cert-file (ks/temp-file) + _ (spit renewed-cert-file renewed-cert-pem) + renewed-cert (ssl-utils/pem->cert renewed-cert-file) + signed-cert (ssl-utils/pem->cert signed-cert-file)] + (testing "certificates are same subject" + (is (= (ssl-utils/get-subject-from-x509-certificate signed-cert) + (ssl-utils/get-subject-from-x509-certificate renewed-cert)))) + (testing "serial number has been incremented" + (is (< (.getSerialNumber signed-cert) (.getSerialNumber renewed-cert)))) + (testing "not before time stamps have changed" + (is (true? (.before (.getNotBefore signed-cert) (.getNotBefore renewed-cert))))) + (testing "new not-after is earlier than before" + (is (true? (.after (.getNotAfter signed-cert) (.getNotAfter renewed-cert))))) + (testing "new not-after should be 41 days (and some fraction) away" + (let [diff (- (.getTime (.getNotAfter renewed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 41 days)))))))) + (testing "respects x-client-cert header when requester is in infra-nodes file" + (let [infra-inventory-path (str bootstrap/server-conf-dir "/ca/infra_inventory.txt") + infra-inventory-content (slurp infra-inventory-path) + ;; We're going to pretend this is an infra cert for this test + compiler "compiler.puppet.com"] + ;; Add another cert to the infra inventory + (spit infra-inventory-path (str infra-inventory-content compiler)) + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true + :auto-renewal-cert-ttl "42d"}} + (let [generated-agent-cert-info (generate-and-sign-a-cert! "agent.puppet.com") + generated-compiler-cert-info (generate-and-sign-a-cert! "compiler.puppet.com") + compiler-signed-cert-file (ks/temp-file) + agent-signed-cert-file (ks/temp-file) + _ (spit compiler-signed-cert-file (:signed-cert generated-compiler-cert-info)) + _ (spit agent-signed-cert-file (:signed-cert generated-agent-cert-info)) + _ (Thread/sleep 1000) ;; ensure some time has passed so the timestamps are different + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" (ring-codec/url-encode (:signed-cert generated-agent-cert-info))} + :ssl-cert (str compiler-signed-cert-file) + :ssl-key (str (:private-key generated-compiler-cert-info)) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 200 (:status response))) + (let [renewed-cert-pem (:body response) + renewed-cert-file (ks/temp-file) + _ (spit renewed-cert-file renewed-cert-pem) + renewed-cert (ssl-utils/pem->cert renewed-cert-file) + signed-cert (ssl-utils/pem->cert agent-signed-cert-file)] + (testing "certificates are the same agent subject" + (is (= (ssl-utils/get-subject-from-x509-certificate signed-cert) + (ssl-utils/get-subject-from-x509-certificate renewed-cert)))) + (testing "serial number has been incremented" + (is (< (.getSerialNumber signed-cert) (.getSerialNumber renewed-cert)))) + (testing "not before time stamps have changed" + (is (true? (.before (.getNotBefore signed-cert) (.getNotBefore renewed-cert))))) + (testing "new not-after is earlier than before" + (is (true? (.after (.getNotAfter signed-cert) (.getNotAfter renewed-cert))))) + (testing "new not-after should be 41 days (and some fraction) away" + (let [diff (- (.getTime (.getNotAfter renewed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 41 days))))))) + ;; restore the content of the inventory file + (spit infra-inventory-path infra-inventory-content))) + + (testing "rejects x-client-cert header when requester revoked" + (let [infra-inventory-path (str bootstrap/server-conf-dir "/ca/infra_inventory.txt") + random-agent-certname (ks/rand-str :alpha-lower 16) + random-compiler-certname (ks/rand-str :alpha-lower 16) + infra-inventory-content (slurp infra-inventory-path)] + ;; We're going to pretend this is an infra cert for this test + (spit infra-inventory-path (str infra-inventory-content random-compiler-certname)) + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true + :auto-renewal-cert-ttl "42d"}} + (let [generated-agent-cert-info (generate-and-sign-a-cert! random-agent-certname) + generated-compiler-cert-info (generate-and-sign-a-cert! random-compiler-certname) + compiler-signed-cert-file (ks/temp-file) + agent-signed-cert-file (ks/temp-file) + _ (spit compiler-signed-cert-file (:signed-cert generated-compiler-cert-info)) + _ (spit agent-signed-cert-file (:signed-cert generated-agent-cert-info)) + revoke-response (http-client/put + (str "https://localhost:8140/puppet-ca/v1/certificate_status/" random-agent-certname) + {:body "{\"desired_state\": \"revoked\"}" + :ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :headers {"content-type" "application/json"}})] + (is (= 204 (:status revoke-response))) + (Thread/sleep 1000) ;; ensure some time has passed so the timestamps are different + (let [response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" (ring-codec/url-encode (:signed-cert generated-agent-cert-info))} + :ssl-cert (str compiler-signed-cert-file) + :ssl-key (str (:private-key generated-compiler-cert-info)) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No valid certificate found in renewal request" (:body response))) + ;; restore the content of the inventory file + (spit infra-inventory-path infra-inventory-content)))))) + + (testing "fails for x-client-cert header when requester is not in infra-nodes file" + (let [infra-inventory-path (str bootstrap/server-conf-dir "/ca/infra_inventory.txt") + infra-inventory-content (slurp infra-inventory-path) + ;; We're going to pretend this is an infra cert for this test + compiler "compiler.puppet.com"] + ;; Add another cert to the infra inventory + (spit infra-inventory-path (str infra-inventory-content compiler)) + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true + :auto-renewal-cert-ttl "42d"}} + (let [generated-agent-cert-info (generate-and-sign-a-cert! "agent.puppet.com") + generated-compiler-cert-info (generate-and-sign-a-cert! "maliciousagent.puppet.com") + compiler-signed-cert-file (ks/temp-file) + agent-signed-cert-file (ks/temp-file) + _ (spit compiler-signed-cert-file (:signed-cert generated-compiler-cert-info)) + _ (spit agent-signed-cert-file (:signed-cert generated-agent-cert-info)) + _ (Thread/sleep 1000) ;; ensure some time has passed so the timestamps are different + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" (ring-codec/url-encode (:signed-cert generated-agent-cert-info))} + :ssl-cert (str compiler-signed-cert-file) + :ssl-key (str (:private-key generated-compiler-cert-info)) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No valid certificate found in renewal request" (:body response))))) + ;; restore the content of the inventory file + (spit infra-inventory-path infra-inventory-content))) + + (testing "fails for x-client-cert header when infra-nodes-file is missing" + (let [infra-inventory-path (str bootstrap/server-conf-dir "/ca/infra_inventory.txt") + infra-inventory-content (slurp infra-inventory-path)] + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true + :auto-renewal-cert-ttl "42d"}} + (io/delete-file infra-inventory-path) + (let [generated-agent-cert-info (generate-and-sign-a-cert! "agent.puppet.com") + generated-compiler-cert-info (generate-and-sign-a-cert! "compiler.puppet.com") + compiler-signed-cert-file (ks/temp-file) + agent-signed-cert-file (ks/temp-file) + _ (spit compiler-signed-cert-file (:signed-cert generated-compiler-cert-info)) + _ (spit agent-signed-cert-file (:signed-cert generated-agent-cert-info)) + _ (Thread/sleep 1000) ;; ensure some time has passed so the timestamps are different + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" (ring-codec/url-encode (:signed-cert generated-agent-cert-info))} + :ssl-cert (str compiler-signed-cert-file) + :ssl-key (str (:private-key generated-compiler-cert-info)) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No valid certificate found in renewal request" (:body response))))) + ;; restore the content of the inventory file + (spit infra-inventory-path infra-inventory-content))) + + (testing "returns a 400 bad request response when the ssl-client-cert is not present" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true}} + (let [response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No valid certificate found in renewal request" (:body response)))))) + (testing "returns a 400 bad request when ssl-client-cert is not present and x-client-cert is specified" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + header-cert (ring-codec/url-encode (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No valid certificate found in renewal request" (:body response))))))) + + (testing "with allow-header-cert-info = true" + (testing "returns a 200 OK response when the feature is enabled, + a cert is supplied in header and the signing certificate matches" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true} + :authorization + {:allow-header-cert-info true}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + header-cert (ring-codec/url-encode (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 200 (:status response))) + (let [renewed-cert-pem (:body response) + renewed-cert-file (ks/temp-file) + _ (spit renewed-cert-file renewed-cert-pem) + renewed-cert (ssl-utils/pem->cert renewed-cert-file) + signed-cert (ssl-utils/pem->cert signed-cert-file)] + (testing "serial number has been incremented" + (is (< (.getSerialNumber signed-cert) (.getSerialNumber renewed-cert)))) + (testing "not before time stamps have changed" + (is (true? (.before (.getNotBefore signed-cert) (.getNotBefore renewed-cert))))) + (testing "new not-after is earlier than before" + (is (true? (.after (.getNotAfter signed-cert) (.getNotAfter renewed-cert))))) + (testing "new not-after should be 89 days (and some fraction) away" + (let [diff (- (.getTime (.getNotAfter renewed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 89 days)))))))) + (testing "returns a 400 bad request response when the feature is enabled, + a revoked cert is supplied in header and the signing certificate matches" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true} + :authorization + {:allow-header-cert-info true}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + ;; revoke the cert + revoke-response (http-client/put + (str "https://localhost:8140/puppet-ca/v1/certificate_status/" random-certname) + {:body "{\"desired_state\": \"revoked\"}" + :ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :headers {"content-type" "application/json"}})] + (is (= 204 (:status revoke-response))) + (let [header-cert (ring-codec/url-encode (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No valid certificate found in renewal request" (:body response))))))) + + (testing "Can revoke a renewed certificate" + (testing "using certificate status" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true} + :authorization + {:allow-header-cert-info true}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + header-cert (ring-codec/url-encode (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 200 (:status response))) + (logutils/with-test-logging + (let [ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + ca-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + ca-crl (str bootstrap/server-conf-dir "/ca/ca_crl.pem") + ca-cert' (ssl-utils/pem->ca-cert ca-cert ca-key) + revoke-response (http-client/put + (str "https://localhost:8140/puppet-ca/v1/certificate_status/" random-certname) + {:body "{\"desired_state\": \"revoked\"}" + :ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :headers {"content-type" "application/json"}})] + (is (= 204 (:status revoke-response))) + (is (ssl-utils/revoked? (ssl-utils/pem->ca-crl ca-crl ca-cert') (ssl-utils/pem->cert signed-cert-file))) + (is (logged? #"Entity CA revoked 1 certificate.*" :info))))))) + + (testing "using clean" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true} + :authorization + {:allow-header-cert-info true}} + (let [random-certname (ks/rand-str :alpha-lower 16) + generated-cert-info (generate-and-sign-a-cert! random-certname) + signed-cert-file (ks/temp-file) + _ (spit signed-cert-file (:signed-cert generated-cert-info)) + header-cert (ring-codec/url-encode (:signed-cert generated-cert-info)) + _ (Thread/sleep 1000) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 200 (:status response))) + (logutils/with-test-logging + (let [ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + ca-key (str bootstrap/server-conf-dir "/ca/ca_key.pem") + ca-crl (str bootstrap/server-conf-dir "/ca/ca_crl.pem") + ca-cert' (ssl-utils/pem->ca-cert ca-cert ca-key) + revoke-response (http-client/put + (str "https://localhost:8140/puppet-ca/v1/clean") + {:body (json/encode {:certnames [random-certname]}) + :ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert ca-cert + :headers {"content-type" "application/json"}})] + (is (= 200 (:status revoke-response))) + (is (ssl-utils/revoked? (ssl-utils/pem->ca-crl ca-crl ca-cert') (ssl-utils/pem->cert signed-cert-file))) + (is (logged? #"Entity CA revoked 1 certificate.*" :info)))))))) + + (testing "returns a 400 bad request response when the feature is enabled, + and a bogus cert is supplied in the header" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true} + :authorization + {:allow-header-cert-info true}} + (let [header-cert "abadstring" + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 400 (:status response))) + (is (= "No certs found in PEM read from x-client-cert" (:body response)))))) + + (testing "returns a 403 forbidden response when a certificate is present in the request, + but that cert does not match the signing cert" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal true} + :authorization + {:allow-header-cert-info true}} + (let [ca-cert (create-ca-cert "ca-nomatch" 42) + ca-cert-file (ks/temp-file) + _ (ssl-utils/cert->pem! (:cert ca-cert) ca-cert-file) + cert-1 (simple/gen-cert "localhost" ca-cert 4 {:extensions [(ssl-utils/authority-key-identifier-options (:cert ca-cert))]}) + cert-1-file (ks/temp-file) + _ (ssl-utils/cert->pem! (:cert cert-1) cert-1-file) + header-cert (ring-codec/url-encode (slurp cert-1-file)) + private-key-file (ks/temp-file) + _ (ssl-utils/key->pem! (:private-key cert-1) private-key-file) + response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:headers {"x-client-cert" header-cert} + :ssl-cert (str cert-1-file) + :ssl-key (str private-key-file) + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 403 (:status response))) + (is (= "Certificate present, but does not match signature" (:body response)))))))) + + (testing "with the feature disabled" + (testing "returns a 404 not found response when feature is disabled" + (bootstrap/with-puppetserver-running-with-mock-jrubies + "JRuby mocking is safe here because all of the requests are to the CA + endpoints, which are implemented in Clojure." + app + {:jruby-puppet + {:gem-path [(ks/absolute-path jruby-testutils/gem-path)]} + :webserver + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :ssl-crl-path (str bootstrap/server-conf-dir "/ssl/crl.pem")} + :certificate-authority + {:allow-auto-renewal false}} + (let [response (http-client/post + "https://localhost:8140/puppet-ca/v1/certificate_renewal" + {:ssl-cert (str bootstrap/server-conf-dir "/ssl/certs/localhost.pem") + :ssl-key (str bootstrap/server-conf-dir "/ssl/private_keys/localhost.pem") + :ssl-ca-cert (str bootstrap/server-conf-dir "/ca/ca_crt.pem") + :as :text})] + (is (= 404 (:status response)))))))) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/class_info_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/class_info_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/class_info_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/class_info_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.jruby.class-info-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [clojure.string :as str] [puppetlabs.kitchensink.core :as ks] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/jruby_metrics_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,26 +1,15 @@ (ns puppetlabs.services.jruby.jruby-metrics-service-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.trapperkeeper.app :as tk-app] [puppetlabs.trapperkeeper.services :as tk-services] [puppetlabs.trapperkeeper.testutils.bootstrap :as bootstrap] [puppetlabs.trapperkeeper.testutils.logging :as logging] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] - [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9-service] [puppetlabs.services.jruby.jruby-puppet-service :as jruby-service] - [puppetlabs.services.jruby-pool-manager.impl.jruby-internal :as jruby-internal] - [puppetlabs.services.puppet-profiler.puppet-profiler-service :as profiler] - [puppetlabs.services.jruby.jruby-metrics-service :as jruby-metrics-service] - [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as scheduler-service] - [puppetlabs.trapperkeeper.services.metrics.metrics-service :as metrics-service] - [puppetlabs.services.request-handler.request-handler-service :as request-handler-service] - [puppetlabs.services.versioned-code-service.versioned-code-service :as versioned-code-service] - [puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service :as jruby-pool-manager-service] [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] [puppetlabs.services.protocols.jruby-metrics :as jruby-metrics-protocol] [puppetlabs.services.protocols.puppet-server-config :as ps-config-protocol] - [puppetlabs.trapperkeeper.services.status.status-service :as status-service] - [puppetlabs.trapperkeeper.services.webrouting.webrouting-service :as webrouting-service] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap-testutils] [schema.test :as schema-test] [puppetlabs.metrics :as metrics] @@ -174,9 +163,9 @@ (let [puppet-config (jruby-testutils/mock-puppet-config-settings (:jruby-puppet config))] (reify JRubyPuppet - (getSetting [_ setting] + (getSetting [_this setting] (get puppet-config setting)) - (handleRequest [this request] + (handleRequest [_this request] ;; read the request-id from the query params so that we can ;; interact with the test coordinator (let [request-id (get-in request ["params" "request-id"])] @@ -421,8 +410,7 @@ :borrow-count 2 :current-borrowed-instances 2}) - (let [expected-metrics (expected-metrics-values) - jruby-status-metrics (current-jruby-status-metrics)] + (let [expected-metrics (expected-metrics-values)] (is (= expected-metrics (current-metrics-values))) (is (= expected-metrics (-> (current-jruby-status-metrics) (jruby-status-metric-counters))))) @@ -517,16 +505,16 @@ ;; manually borrow an instance, but do it on another thread because ;; we know it will block right now due to the empty pool - (let [future-instance (promise) - wait-to-return (promise) - future-thread (future - (let [instance (jruby-testutils/borrow-instance - jruby-service :metrics-manual-borrow-test2)] - (deliver future-instance instance) - @wait-to-return - (jruby-testutils/return-instance - jruby-service instance - :metrics-manual-borrow-test2))) + (let [instance-borrowed (promise) + return-instance (promise) + pool-worker (future + (let [instance (jruby-testutils/borrow-instance + jruby-service :metrics-manual-borrow-test2)] + (deliver instance-borrowed instance) + @return-instance + (jruby-testutils/return-instance + jruby-service instance + :metrics-manual-borrow-test2))) expected-request-count (inc (:requested-count (expected-metrics-values)))] ;; wait for manual borrow attempt to register as a requested instance (while (> expected-request-count (.getCount requested-count))) @@ -543,20 +531,21 @@ (coordinator/final-result coordinator 1) (coordinator/final-result coordinator 2) - (let [instance @future-instance] - (update-expected-values {:num-free-jrubies 1 - :return-count 2 - :borrow-count 1 - :current-borrowed-instances -1 - :current-requested-instances -1}) - (is (= (expected-metrics-values) (current-metrics-values))) - - (let [borrowed (first (vals @borrowed-instances))] - (is (timestamp-after? start-time (:time borrowed))) - (is (= :metrics-manual-borrow-test2 (:reason borrowed)))) + @instance-borrowed + + (update-expected-values {:num-free-jrubies 1 + :return-count 2 + :borrow-count 1 + :current-borrowed-instances -1 + :current-requested-instances -1}) + (is (= (expected-metrics-values) (current-metrics-values))) + + (let [borrowed (first (vals @borrowed-instances))] + (is (timestamp-after? start-time (:time borrowed))) + (is (= :metrics-manual-borrow-test2 (:reason borrowed)))) - (deliver wait-to-return true) - @future-thread)) + (deliver return-instance true) + @pool-worker) (update-expected-values {:num-free-jrubies 1 :return-count 1 @@ -604,7 +593,7 @@ ;; will be empty.) (coordinator/unblock-task-to coordinator request-id :borrowed-jruby))) - run-request-and-take-sample (fn [request-id phase] + run-request-and-take-sample (fn [request-id _phase] ;; block until the request has borrowed a jruby; we need ;; to do this since we told them they were unblocked to ;; that point. @@ -660,7 +649,7 @@ (let [initial-value (metrics/mean free-jrubies-histo)] ;; take 10 samples; no jrubies are in use, so the average ;; should increase with each sample. - (let [samples (for [i (range 10)] + (let [samples (for [_ (range 10)] (do (sample-metrics!) (metrics/mean free-jrubies-histo)))] @@ -672,7 +661,7 @@ (testing "requested-jrubies histo decreases when requests are not queued" (let [initial-value (metrics/mean requested-jrubies-histo)] ;; take 10 samples; the average should increase with each sample. - (let [samples (for [i (range 10)] + (let [samples (for [_ (range 10)] (do (sample-metrics!) (metrics/mean requested-jrubies-histo)))] @@ -714,7 +703,7 @@ ;; take some samples and validate that the histogram is ;; increasing. (let [initial-value (metrics/mean requested-jrubies-histo)] - (let [samples (for [i (range 10)] + (let [samples (for [_ (range 10)] (do (sample-metrics!) (metrics/mean requested-jrubies-histo)))] @@ -753,7 +742,7 @@ (is (= 0 (.getValue num-free-jrubies))) ;; take 10 samples; the average should decrease with each sample. - (let [samples (for [i (range 10)] + (let [samples (for [_ (range 10)] (do (sample-metrics!) (metrics/mean free-jrubies-histo)))] @@ -806,18 +795,18 @@ {:keys [borrow-timer]} (:metrics test-env)] (testing "borrow timer increases when requests are slower" (dotimes [request-id 10] - (let [last-borrow-time (metrics/mean-millis borrow-timer)] - (let [longer-borrow-time (+ 50 last-borrow-time)] - (sync-request coordinator request-id (format "/foo/bar/req?sleep=%s" longer-borrow-time)) - ;; sample and validate that the borrow time has increased - (sample-metrics!) - (let [new-borrow-time (metrics/mean-millis borrow-timer)] - (is (<= last-borrow-time new-borrow-time) - (format - (str "Borrow time did not increase! " - "last-borrow-time: '%s', longer-borrow-time: '%s', " - "new-borrow-time: '%s'") - last-borrow-time longer-borrow-time new-borrow-time)))))) + (let [last-borrow-time (metrics/mean-millis borrow-timer) + longer-borrow-time (+ 50 last-borrow-time)] + (sync-request coordinator request-id (format "/foo/bar/req?sleep=%s" longer-borrow-time)) + ;; sample and validate that the borrow time has increased + (sample-metrics!) + (let [new-borrow-time (metrics/mean-millis borrow-timer)] + (is (<= last-borrow-time new-borrow-time) + (format + (str "Borrow time did not increase! " + "last-borrow-time: '%s', longer-borrow-time: '%s', " + "new-borrow-time: '%s'") + last-borrow-time longer-borrow-time new-borrow-time))))) (update-expected-values {:requested-count 10 :borrow-count 10 @@ -926,9 +915,8 @@ (-> default-test-config (assoc-in [:jruby-puppet :max-active-instances] 2) (assoc-in [:jruby-puppet :max-queued-requests] 2)) - (let [{:keys [current-metrics-values coordinator]} test-env - {:keys [requested-count requested-instances - borrowed-instances queue-limit-hit-meter]} (:metrics test-env)] + (let [{:keys [coordinator]} test-env + {:keys [requested-count queue-limit-hit-meter]} (:metrics test-env)] (logging/with-test-logging ;; Block up two JRuby instances (async-request coordinator 1 "/foo/bar/async1" :returning-jruby) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/jruby_puppet_pool_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.jruby.jruby-puppet-pool-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [are deftest is testing use-fixtures]] [clojure.java.io :as io] [clojure.set :as set] [clojure.tools.logging :as log] @@ -11,14 +11,13 @@ [puppetlabs.kitchensink.testutils :as ks-testutils] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.puppetserver.testutils :as testutils :refer - [ca-cert localhost-cert localhost-key ssl-request-options]] + [ssl-request-options]] [puppetlabs.services.config.puppet-server-config-service :as ps-config] [puppetlabs.services.jruby.jruby-metrics-service :as jruby-metrics-service] [puppetlabs.services.jruby.jruby-puppet-core :as jruby-puppet-core] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.services.jruby-pool-manager.impl.jruby-agents :as jruby-agents] [puppetlabs.services.jruby-pool-manager.jruby-core :as jruby-core] - [puppetlabs.services.jruby-pool-manager.jruby-schemas :as jruby-schemas] [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] [puppetlabs.services.protocols.request-handler :as handler] [puppetlabs.services.request-handler.request-handler-core :as handler-core] @@ -31,14 +30,12 @@ [puppetlabs.trapperkeeper.internal :as tk-internal] [puppetlabs.trapperkeeper.services :as tk-services] [puppetlabs.trapperkeeper.services.protocols.metrics :as metrics-protocol] - [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-testutils] [puppetlabs.trapperkeeper.testutils.logging :as logutils]) (:import (com.codahale.metrics MetricRegistry) (java.io ByteArrayOutputStream) (org.jruby RubyInstanceConfig$CompileMode RubyInstanceConfig$ProfilingMode) - (org.jruby.embed EvalFailedException) - (org.jruby.runtime Constants))) + (org.jruby.embed EvalFailedException))) (def test-resources-dir "./dev-resources/puppetlabs/services/jruby/jruby_pool_int_test") @@ -102,7 +99,7 @@ [pool-context] (let [flush-complete (promise)] (add-watch (get-in pool-context [:internal :modify-instance-agent]) :flush-callback - (fn [k a old-state new-state] + (fn [k a _old-state _new-state] (when (= k :flush-callback) (remove-watch a :flush-callback) (deliver flush-complete true)))) @@ -162,7 +159,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Tests -(deftest ^:integration ^:single-threaded-only admin-api-flush-jruby-pool-test +(deftest ^:integration ^:single-threaded-only admin-api-flush-jruby-pool-single-threaded-test (testing "Flushing the instance pool results in all new JRuby instances" (bootstrap/with-puppetserver-running app @@ -183,13 +180,14 @@ (is (true? (verify-no-constants pool-context 4))))))) ;; Copies the test above, but for multithreaded mode (single jruby instance) -(deftest ^:integration ^:multithreaded-only admin-api-flush-jruby-ref-pool-test +(deftest ^:integration ^:multithreaded-only admin-api-flush-jruby-ref-pool-multi-threaded-test (testing "Flushing the reference pool results in a new JRuby instance" (bootstrap/with-puppetserver-running app {:jruby-puppet {:gem-path gem-path :max-active-instances 4 - :borrow-timeout default-borrow-timeout}} + :borrow-timeout default-borrow-timeout + :multithreaded true}} (let [jruby-service (tk-app/get-service app :JRubyPuppetService) context (tk-services/service-context jruby-service) pool-context (:pool-context context)] @@ -475,8 +473,7 @@ (is (= [] (:url-and-method metrics-data))))) (testing "POST request" (.runScriptlet container (format "$c.post(URI('%s'), 'body')" url)) - (let [metrics-data (metrics/get-client-metrics-data metric-registry) - url-metrics-data (:url metrics-data)] + (let [metrics-data (metrics/get-client-metrics-data metric-registry)] (is (= [] (:url metrics-data))) (is (= [] (:url-and-method metrics-data))))) (testing "no metric-id metrics are registered" @@ -640,7 +637,7 @@ script "require 'timeout'; Timeout::timeout(0.1) { sleep(5) }"] (try (.runScriptlet scripting-container script) - (catch EvalFailedException e)) + (catch EvalFailedException _)) (let [threads-during-jruby (set (keys (Thread/getAllStackTraces)))] (jruby-testutils/return-instance jruby-service instance :test) (jruby-protocol/flush-jruby-pool! jruby-service) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/module_info_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/module_info_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/module_info_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/module_info_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.jruby.module-info-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [puppetlabs.kitchensink.core :as ks] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [me.raynes.fs :as fs] @@ -51,7 +51,6 @@ (let [jruby-service (tk-app/get-service app :JRubyPuppetService) instance (jruby-testutils/borrow-instance jruby-service :test) jruby-puppet (:jruby-puppet instance) - env-registry (:environment-registry instance) _ (testutils/create-file (fs/file conf-dir "puppet.conf") "[main]\nenvironment_timeout=unlimited\nbasemodulepath=$codedir/modules\n") diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/puppet_environments_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/puppet_environments_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/puppet_environments_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/puppet_environments_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,10 +1,10 @@ (ns puppetlabs.services.jruby.puppet-environments-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.http.client.sync :as http-client] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.puppetserver.testutils :as testutils :refer - [ssl-request-options catalog-request-options get-catalog]] + [ssl-request-options get-catalog]] [me.raynes.fs :as fs] [puppetlabs.trapperkeeper.app :as tk-app] [puppetlabs.kitchensink.core :as ks])) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/request_handler_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/request_handler_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/request_handler_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/request_handler_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -4,15 +4,14 @@ (org.apache.commons.io IOUtils)) (:require [clojure.string :as string] [clojure.java.io :as io] - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [me.raynes.fs :as fs] [puppetlabs.kitchensink.core :as ks] [schema.test :as schema-test] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.http.client.sync :as http-client] - [puppetlabs.puppetserver.testutils :as testutils :refer - [ca-cert localhost-cert localhost-key ssl-request-options]])) + [puppetlabs.puppetserver.testutils :as testutils :refer [ssl-request-options]])) (def test-resources-dir "./dev-resources/puppetlabs/services/jruby/request_handler_test") @@ -62,7 +61,7 @@ (is (fs/exists? expected-bucket-file) "Bucket file not stored at expected location") (is (= (seq raw-byte-arr) - (if (fs/exists? expected-bucket-file) + (when (fs/exists? expected-bucket-file) (-> expected-bucket-file (io/input-stream) (IOUtils/toByteArray) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/service_macros_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/service_macros_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/service_macros_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/service_macros_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.jruby.service-macros-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [puppetlabs.trapperkeeper.app :as tk-app] [puppetlabs.services.jruby.jruby-puppet-service :as jruby-puppet] [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-testutils] diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/tasks_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/tasks_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/jruby/tasks_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/jruby/tasks_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,10 +1,9 @@ (ns puppetlabs.services.jruby.tasks-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [clojure.string :as str] - [clojure.pprint :refer :all] + [clojure.walk :refer [keywordize-keys]] [puppetlabs.kitchensink.core :as ks] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] - [puppetlabs.services.jruby.puppet-environments :as puppet-env] [puppetlabs.services.master.master-core :as mc] [me.raynes.fs :as fs] [cheshire.core :as cheshire] @@ -113,7 +112,7 @@ [tasks] (map (fn [{:keys [name module-name metadata]}] {:module {:name module-name} - :metadata (clojure.walk/keywordize-keys metadata) + :metadata (keywordize-keys metadata) :name (if (= "init" name) module-name (str module-name "::" name))}) @@ -310,9 +309,9 @@ (get-task-details "production" "apache" "init"))))) (testing "with no payload files" - (let [expected-info {:metadata {:meta "data"} - :name "apache::about" - :files []}] + (let [_expected-info {:metadata {:meta "data"} + :name "apache::about" + :files []}] (is (thrown+? [:kind "puppet.tasks/no-implementation"] (get-task-details "production" "apache" "about")))))) (testing "but the task doesn't exist" diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/master/environment_classes_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/master/environment_classes_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/master/environment_classes_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/master/environment_classes_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.master.environment-classes-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [clojure.string :as str] [puppetlabs.http.client.sync :as http-client] [puppetlabs.kitchensink.core :as ks] @@ -7,7 +7,7 @@ [puppetlabs.puppetserver.testutils :as testutils] [puppetlabs.trapperkeeper.app :as tk-app] [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-bootstrap-testutils] - [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] + [puppetlabs.trapperkeeper.testutils.webserver :as jetty10] [puppetlabs.services.master.master-core :as master-core] [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] [cheshire.core :as cheshire] @@ -45,7 +45,7 @@ ([env-name] (get-env-classes env-name nil)) ([env-name if-none-match] - (let [opts (if if-none-match + (let [opts (when if-none-match {:headers {"If-None-Match" if-none-match}})] (try (http-client/get @@ -143,40 +143,39 @@ :jruby-puppet {:gem-path gem-path :max-active-instances 1 :environment-class-cache-enabled true}} - (try - (let [foo-file (testutils/write-pp-file - "class foo (String $foo_1 = \"is foo\"){}" - "foo") - bar-file (testutils/write-pp-file - "class foo::bar (Integer $foo_2 = 3){}" - "bar") - initial-response (get-env-classes "production") - initial-etag (-> initial-response response-etag without-gzip-suffix)] - (testing "initial fetch of environment_classes info is good" - (is (= 200 (:status initial-response)) + (let [_foo-file (testutils/write-pp-file + "class foo (String $foo_1 = \"is foo\"){}" + "foo") + _bar-file (testutils/write-pp-file + "class foo::bar (Integer $foo_2 = 3){}" + "bar") + initial-response (get-env-classes "production") + initial-etag (-> initial-response response-etag without-gzip-suffix)] + (testing "initial fetch of environment_classes info is good" + (is (= 200 (:status initial-response)) + (str + "unexpected status code for initial response, response: " + (ks/pprint-to-string initial-response))) + (is (not (nil? initial-etag)) + "no etag found for initial response")) + (testing "ensure the env cache is purged" + (purge-env-cache "production") + (is (= nil + (jruby-protocol/get-cached-info-tag + (tk-app/get-service app :JRubyPuppetService) + "production" + :classes)))) + (testing "Env cache is updated when given correct etag but cache was nil" + (let [next-response (get-env-classes "production" initial-etag)] + (is (= 304 (:status next-response)) (str "unexpected status code for initial response, response: " (ks/pprint-to-string initial-response))) - (is (not (nil? initial-etag)) - "no etag found for initial response")) - (testing "ensure the env cache is purged" - (purge-env-cache "production") - (= nil - (jruby-protocol/get-cached-info-tag - (tk-app/get-service app :JRubyPuppetService) - "production" - :classes))) - (testing "Env cache is updated when given correct etag but cache was nil" - (let [next-response (get-env-classes "production" initial-etag)] - (is (= 304 (:status next-response)) - (str - "unexpected status code for initial response, response: " - (ks/pprint-to-string initial-response))) - (is (= initial-etag - (jruby-protocol/get-cached-info-tag - (tk-app/get-service app :JRubyPuppetService) - "production" - :classes)))))))))) + (is (= initial-etag + (jruby-protocol/get-cached-info-tag + (tk-app/get-service app :JRubyPuppetService) + "production" + :classes))))))))) (deftest ^:integration environment-classes-integration-cache-enabled-test (let [debug-log "./target/environment-classes-integration-cache-enabled.log"] @@ -604,9 +603,7 @@ (deftest ^:integration not-modified-returned-for-environment-class-info-request-with-gzip-tag - (testing (str "SERVER-1153 - when the webserver gzips the response " - "containing environment_classes etag, the next request " - "roundtripping that etag returns an HTTP 304 (Not Modified)") + (testing "SERVER-1153 - when the webserver gzips the response containing environment_classes etag, the next request roundtripping that etag returns an HTTP 304 (Not Modified)" (let [expected-etag "abcd1234" body-length 200000 jruby-service (reify jruby-protocol/JRubyPuppetService @@ -618,7 +615,7 @@ (apply str (repeat body-length "a")) expected-etag)) (master-core/wrap-with-cache-check jruby-service))] - (jetty9/with-test-webserver + (jetty10/with-test-webserver app port (let [request-url (str "http://localhost:" port) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/master/environment_modules_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/master/environment_modules_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/master/environment_modules_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/master/environment_modules_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.master.environment-modules-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.http.client.sync :as http-client] [puppetlabs.kitchensink.core :as ks] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] @@ -70,14 +70,14 @@ (ks/dissoc-in [:jruby-puppet :environment-class-cache-enabled])) (logutils/with-test-logging - (let [foo-file (testutils/write-foo-pp-file - "class foo (String $foo_1 = \"is foo\"){}") - all-foo-file (testutils/write-pp-file - "class foo (String $foo_1 = \"is foo\"){}" - "foo" - "woo" - "hoo" - "1.0.0") + (let [_foo-file (testutils/write-foo-pp-file + "class foo (String $foo_1 = \"is foo\"){}") + _all-foo-file (testutils/write-pp-file + "class foo (String $foo_1 = \"is foo\"){}" + "foo" + "woo" + "hoo" + "1.0.0") expected-response {"modules" [{"name" "foo", "version" "1.0.0"}] "name" "production"} expected-all-response '({"modules" diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/master/environment_transports_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/master/environment_transports_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/master/environment_transports_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/master/environment_transports_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,13 +1,12 @@ (ns puppetlabs.services.master.environment-transports-int-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [clojure.string :as str] - [clojure.walk :as walk] [puppetlabs.http.client.sync :as http-client] [puppetlabs.kitchensink.core :as ks] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] [puppetlabs.puppetserver.testutils :as testutils] [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-bootstrap-testutils] - [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] + [puppetlabs.trapperkeeper.testutils.webserver :as jetty10] [puppetlabs.services.master.master-core :as master-core] [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] [cheshire.core :as json] @@ -15,7 +14,7 @@ [clojure.tools.logging :as log] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils]) (:import (com.puppetlabs.puppetserver JRubyPuppetResponse JRubyPuppet) - (java.util HashMap ArrayList))) + (java.util ArrayList))) (def test-resources-dir "./dev-resources/puppetlabs/services/master/environment_transports_int_test") @@ -127,7 +126,7 @@ ([env-name] (get-env-transports env-name nil)) ([env-name if-none-match] - (let [opts (if if-none-match + (let [opts (when if-none-match {:headers {"If-None-Match" if-none-match}})] (try (http-client/get @@ -290,10 +289,7 @@ "production" production-etag-initial) production-etag-after-prod-flush (response-etag - production-response-after-prod-flush) - test-response-after-prod-flush (get-env-transports - "test" - test-etag-initial)] + production-response-after-prod-flush)] (is (= 200 (:status production-response-after-prod-flush)) (str "unexpected status code for prod response after code change " @@ -477,9 +473,7 @@ (deftest ^:integration not-modified-returned-for-environment-transports-info-request-with-gzip-tag - (testing (str "SERVER-1153 - when the webserver gzips the response " - "containing environment_transports etag, the next request " - "roundtripping that etag returns an HTTP 304 (Not Modified)") + (testing "SERVER-1153 - when the webserver gzips the response containing environment_transports etag, the next request roundtripping that etag returns an HTTP 304 (Not Modified)" (let [expected-etag "abcd1234" body-length 200000 jruby-service (reify jruby-protocol/JRubyPuppetService @@ -491,7 +485,7 @@ (apply str (repeat body-length "a")) expected-etag)) (master-core/wrap-with-cache-check jruby-service))] - (jetty9/with-test-webserver + (jetty10/with-test-webserver app port (let [request-url (str "http://localhost:" port) diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/master/master_service_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/master/master_service_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/master/master_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/master/master_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,7 +1,7 @@ (ns puppetlabs.services.master.master-service-test (:require - [clojure.test :refer :all] - [puppetlabs.services.master.master-service :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] + ; [puppetlabs.services.master.master-service :refer :all] [cheshire.core :as json] [clojure.set :as setutils] [clojure.string :as str] @@ -210,7 +210,7 @@ (is (contains? jruby-metrics :borrow-timers)) (is (= #{:total :puppet-v3-catalog :puppet-v3-node} (set (keys (:borrow-timers jruby-metrics))))) - (doseq [[k v] (:borrow-timers jruby-metrics)] + (doseq [[_k v] (:borrow-timers jruby-metrics)] (is (nil? (schema/check jruby-metrics-core/TimerSummary v)))))) (is (= 1 (get-in status [:puppet-profiler :service_status_version]))) @@ -246,7 +246,7 @@ ;; which should cause a subsequent catalog request to block on a borrow ;; request (jruby-service/with-jruby-puppet - _ + jruby-puppet jruby-service :master-service-metrics-test (let [time-before-second-borrow (System/currentTimeMillis)] @@ -429,29 +429,29 @@ non-heap-memory-map (get-memory-map "non-heap") total-memory-map (get-memory-map "total")] (testing "heap memory metrics work" - (= #{"puppetlabs.localhost.memory.heap.committed" - "puppetlabs.localhost.memory.heap.init" - "puppetlabs.localhost.memory.heap.max" - "puppetlabs.localhost.memory.heap.used"} (ks/keyset heap-memory-map)) + (is (= #{"puppetlabs.localhost.memory.heap.committed" + "puppetlabs.localhost.memory.heap.init" + "puppetlabs.localhost.memory.heap.max" + "puppetlabs.localhost.memory.heap.used"} (ks/keyset heap-memory-map))) (is (every? #(< 0 %) (vals heap-memory-map)))) (testing "non-heap memory metrics work" - (= #{"puppetlabs.localhost.memory.non-heap.committed" - "puppetlabs.localhost.memory.non-heap.init" - "puppetlabs.localhost.memory.non-heap.max" - "puppetlabs.localhost.memory.non-heap.used"} - (ks/keyset non-heap-memory-map)) + (is (= #{"puppetlabs.localhost.memory.non-heap.committed" + "puppetlabs.localhost.memory.non-heap.init" + "puppetlabs.localhost.memory.non-heap.max" + "puppetlabs.localhost.memory.non-heap.used"} + (ks/keyset non-heap-memory-map))) ;; Some of the memory metrics don't propagate correctly on OS X, which can result in a ;; value of -1. This is here so that these tests will pass when run in developers local ;; environments. (is (every? #(or (< 0 %) (= -1 %)) (vals non-heap-memory-map)))) (testing "total memory metrics work" - (= #{"puppetlabs.localhost.memory.total.committed" - "puppetlabs.localhost.memory.total.init" - "puppetlabs.localhost.memory.total.max" - "puppetlabs.localhost.memory.total.used"} - (ks/keyset total-memory-map)) + (is (= #{"puppetlabs.localhost.memory.total.committed" + "puppetlabs.localhost.memory.total.init" + "puppetlabs.localhost.memory.total.max" + "puppetlabs.localhost.memory.total.used"} + (ks/keyset total-memory-map))) (is (every? #(< 0 %) (vals total-memory-map)))) (testing "uptime metric works" @@ -548,8 +548,8 @@ (make-request-with-metric-id "['puppetdb', 'facts', 'find', 'foonode']") (make-request-with-metric-id "['puppetdb', 'facts', 'search']") (make-request-with-metric-id "['puppetdb', 'resource', 'search', 'Package']") - (= (set master-core/puppet-server-http-client-metrics-for-status) - (set (map :metric-id (get-http-client-metrics-status))))) + (is (= (set master-core/puppet-server-http-client-metrics-for-status) + (set (map :metric-id (get-http-client-metrics-status)))))) (testing "all metrics contain information they are supposed to have" (let [metrics (get-http-client-metrics-status)] @@ -597,7 +597,7 @@ (deftest ^:integration add-metric-ids-to-http-client-metrics-list-test (let [test-service (tk-services/service [[:MasterService add-metric-ids-to-http-client-metrics-list!]] - (init [this context] + (init [_this context] (add-metric-ids-to-http-client-metrics-list! [["foo" "bar"] ["hello" "cruel" "world"]]) context))] @@ -694,10 +694,21 @@ (finally (jruby-testutils/return-instance jruby-service jruby-instance :http-report-processor-metrics-test))))))) +(deftest ^:integration compiler-name-as-header + (testing "POSTs to the v3 catalog endpoint return the certname as a header" + (bootstrap-testutils/with-puppetserver-running + app + {:jruby-puppet {:gem-path gem-path + :max-active-instances 1 + :server-code-dir test-resources-code-dir + :server-conf-dir master-service-test-runtime-dir}} + (let [resp (http-post "/puppet/v3/catalog/foo?environment=production" "")] + (is (= "localhost" (get-in resp [:headers "x-puppet-compiler-name"]))))))) + (deftest encoded-spaces-test (testing "Encoded spaces should be routed correctly" (bootstrap-testutils/with-puppetserver-running - _ + app {:jruby-puppet {:gem-path gem-path :max-active-instances 1 :server-conf-dir master-service-test-runtime-dir}} diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/master/plans_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/master/plans_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/master/plans_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/master/plans_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,6 @@ (ns puppetlabs.services.master.plans-int-test (:require [clojure.string :as str] - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.http.client.sync :as http-client] [puppetlabs.kitchensink.core :as ks] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] @@ -39,7 +39,7 @@ (defn get-all-plans [env-name] (let [url (str "https://localhost:8140/puppet/v3/plans" - (if env-name (str "?environment=" env-name)))] + (when env-name (str "?environment=" env-name)))] (try (http-client/get url request-as-text-with-ssl) (catch Exception e @@ -50,7 +50,7 @@ [env-name full-plan-name] (let [[module-name plan-name] (str/split full-plan-name #"::") url (str "https://localhost:8140/puppet/v3/plans/" module-name "/" plan-name - (if env-name (str "?environment=" env-name)))] + (when env-name (str "?environment=" env-name)))] (try (http-client/get url request-as-text-with-ssl) (catch Exception e diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/master/tasks_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/master/tasks_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/master/tasks_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/master/tasks_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,6 @@ (ns puppetlabs.services.master.tasks-int-test (:require [clojure.string :as str] - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.http.client.sync :as http-client] [puppetlabs.kitchensink.core :as ks] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] @@ -39,7 +39,7 @@ (defn get-all-tasks [env-name] (let [url (str "https://localhost:8140/puppet/v3/tasks" - (if env-name (str "?environment=" env-name)))] + (when env-name (str "?environment=" env-name)))] (try (http-client/get url request-as-text-with-ssl) (catch Exception e @@ -50,7 +50,7 @@ [env-name full-task-name] (let [[module-name task-name] (str/split full-task-name #"::") url (str "https://localhost:8140/puppet/v3/tasks/" module-name "/" task-name - (if env-name (str "?environment=" env-name)))] + (when env-name (str "?environment=" env-name)))] (try (http-client/get url request-as-text-with-ssl) (catch Exception e diff -Nru puppetserver-7.9.5/test/integration/puppetlabs/services/puppet_admin/puppet_admin_int_test.clj puppetserver-8.4.0/test/integration/puppetlabs/services/puppet_admin/puppet_admin_int_test.clj --- puppetserver-7.9.5/test/integration/puppetlabs/services/puppet_admin/puppet_admin_int_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/integration/puppetlabs/services/puppet_admin/puppet_admin_int_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,6 @@ (ns puppetlabs.services.puppet-admin.puppet-admin-int-test (:require - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.kitchensink.core :as ks] [puppetlabs.http.client.sync :as http-client] [puppetlabs.puppetserver.bootstrap-testutils :as bootstrap] @@ -8,8 +8,7 @@ [schema.test :as schema-test] [me.raynes.fs :as fs] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] - [puppetlabs.puppetserver.testutils :as testutils :refer - [ca-cert localhost-cert localhost-key ssl-request-options]] + [puppetlabs.puppetserver.testutils :as testutils :refer [ssl-request-options]] [puppetlabs.trapperkeeper.app :as tk-app] [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] [puppetlabs.services.jruby-pool-manager.jruby-core :as jruby-core]) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj puppetserver-8.4.0/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/puppetserver/certificate_authority_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,26 +1,37 @@ (ns puppetlabs.puppetserver.certificate-authority-test - (:import (java.io StringReader - StringWriter - ByteArrayInputStream - ByteArrayOutputStream) - (com.puppetlabs.ssl_utils SSLUtils) - (java.security PublicKey MessageDigest) - (org.joda.time DateTime Period) - (org.bouncycastle.asn1.x509 SubjectPublicKeyInfo)) - (:require [puppetlabs.puppetserver.certificate-authority :refer :all] - [puppetlabs.trapperkeeper.testutils.logging :as logutils] - [puppetlabs.ssl-utils.core :as utils] + (:require [clj-time.coerce :as time-coerce] + [clj-time.core :as time] + [clojure.java.io :as io] + [clojure.set :as set] + [clojure.string :as string] + [clojure.test :refer [deftest is testing use-fixtures]] + [me.raynes.fs :as fs] + [puppetlabs.kitchensink.core :as ks] + [puppetlabs.kitchensink.file :as ks-file] + [puppetlabs.puppetserver.certificate-authority :as ca] + [puppetlabs.puppetserver.common :as common] [puppetlabs.services.ca.ca-testutils :as testutils] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] - [puppetlabs.kitchensink.core :as ks] - [slingshot.test :refer :all] + [puppetlabs.ssl-utils.core :as utils] + [puppetlabs.ssl-utils.simple :as simple] + [puppetlabs.trapperkeeper.testutils.logging :refer [logged?] :as logutils] [schema.test :as schema-test] - [clojure.test :refer :all] - [clojure.java.io :as io] - [clojure.string :as string] - [clj-time.core :as time] - [clj-time.coerce :as time-coerce] - [me.raynes.fs :as fs])) + [slingshot.test :refer :all]) + (:import (com.puppetlabs.ssl_utils SSLUtils) + (java.io ByteArrayInputStream + ByteArrayOutputStream + File StringReader + StringWriter) + (java.nio.file Files Path) + (java.nio.file.attribute FileAttribute) + (java.security MessageDigest PublicKey) + (java.security.cert X509CRL X509Certificate) + (java.time LocalDateTime ZoneOffset) + (java.util Date) + (java.util.concurrent TimeUnit) + (java.util.concurrent.locks ReentrantReadWriteLock) + (org.bouncycastle.asn1.x509 SubjectPublicKeyInfo) + (org.joda.time DateTime Period))) (use-fixtures :once schema-test/validate-schemas) @@ -135,7 +146,7 @@ (def empty-stream (ByteArrayInputStream. (.getBytes ""))) (defn csr-stream [subject] - (io/input-stream (path-to-cert-request csrdir subject))) + (io/input-stream (ca/path-to-cert-request csrdir subject))) (defn write-to-stream [o] (let [s (ByteArrayOutputStream.)] @@ -144,11 +155,11 @@ (defn assert-autosign [whitelist subject] (testing subject - (is (true? (autosign-csr? whitelist subject empty-stream))))) + (is (true? (ca/autosign-csr? whitelist subject empty-stream))))) (defn assert-no-autosign [whitelist subject] (testing subject - (is (false? (autosign-csr? whitelist subject empty-stream))))) + (is (false? (ca/autosign-csr? whitelist subject empty-stream))))) (defn contains-ext? "Does the provided extension list contain an extensions with the given OID." @@ -181,7 +192,7 @@ ssldir (str tmpdir "puppet/ssl")] (fs/mkdirs cadir) (fs/mkdirs ssldir) - (symlink-cadir cadir) + (ca/symlink-cadir cadir) (is (not (fs/exists? (str ssldir "/ca")))))) (testing "symlinks correctly and removes existing old-cadir if needed" (let [tmpdir (ks/temp-dir) @@ -190,7 +201,7 @@ old-cadir (str ssldir "/ca")] (fs/mkdirs ssldir) (fs/mkdirs cadir) - (symlink-cadir cadir) + (ca/symlink-cadir cadir) (is (fs/link? old-cadir)) (let [target (-> old-cadir fs/read-sym-link str)] (is (= target cadir)))))) @@ -200,9 +211,9 @@ (let [settings (assoc (testutils/ca-settings cadir) :ca-ttl - (+ max-ca-ttl 1))] + (+ ca/max-ca-ttl 1))] (is (thrown-with-msg? IllegalStateException #"ca_ttl must have a value below" - (validate-settings! settings))))) + (ca/validate-settings! settings))))) (testing "warns if :client-whitelist is set in c-a.c-s section" (let [settings (assoc-in @@ -210,7 +221,7 @@ [:access-control :certificate-status :client-whitelist] ["whitelist"])] (logutils/with-test-logging - (validate-settings! settings) + (ca/validate-settings! settings) (is (logutils/logged? #"Remove these settings and create" :warn))))) (testing "warns if :authorization-required is overridden in c-a.c-s section" @@ -221,7 +232,7 @@ :authorization-required] false)] (logutils/with-test-logging - (validate-settings! settings) + (ca/validate-settings! settings) (is (logutils/logged? #"Remove these settings and create" :warn))))) (testing "warns if :client-whitelist is set incorrectly" @@ -230,43 +241,43 @@ [:access-control :certificate-status :client-whitelist] [])] (logutils/with-test-logging - (validate-settings! settings) + (ca/validate-settings! settings) (is (logutils/logged? #"remove the 'certificate-authority' configuration" :warn)))))) (deftest get-certificate-test (testing "returns CA certificate when subject is 'ca'" - (let [actual (get-certificate "ca" cacert signeddir) + (let [actual (ca/get-certificate "ca" cacert signeddir) expected (slurp cacert)] (is (= expected actual)))) (testing "returns localhost certificate when subject is 'localhost'" - (let [localhost-cert (get-certificate "localhost" cacert signeddir) - expected (slurp (path-to-cert signeddir "localhost"))] + (let [localhost-cert (ca/get-certificate "localhost" cacert signeddir) + expected (slurp (ca/path-to-cert signeddir "localhost"))] (is (= expected localhost-cert)))) (testing "returns nil when certificate not found for subject" - (is (nil? (get-certificate "not-there" cacert signeddir))))) + (is (nil? (ca/get-certificate "not-there" cacert signeddir))))) (deftest get-certificate-request-test (testing "returns certificate request for subject" - (let [cert-req (get-certificate-request "test-agent" csrdir) - expected (slurp (path-to-cert-request csrdir "test-agent"))] + (let [cert-req (ca/get-certificate-request "test-agent" csrdir) + expected (slurp (ca/path-to-cert-request csrdir "test-agent"))] (is (= expected cert-req)))) (testing "returns nil when certificate request not found for subject" - (is (nil? (get-certificate-request "not-there" csrdir))))) + (is (nil? (ca/get-certificate-request "not-there" csrdir))))) (deftest autosign-csr?-test (testing "boolean values" - (is (true? (autosign-csr? true "unused" empty-stream))) - (is (false? (autosign-csr? false "unused" empty-stream)))) + (is (true? (ca/autosign-csr? true "unused" empty-stream))) + (is (false? (ca/autosign-csr? false "unused" empty-stream)))) (testing "whitelist" (testing "autosign is false when whitelist doesn't exist" - (is (false? (autosign-csr? "Foo/conf/autosign.conf" "doubleagent" - empty-stream)))) + (is (false? (ca/autosign-csr? "Foo/conf/autosign.conf" "doubleagent" + empty-stream)))) (testing "exact certnames" (doto (tmp-whitelist! "foo" @@ -332,12 +343,11 @@ invalid-line "qux")] (assert-autosign whitelist "foo") - (logutils/with-log-output logs + (logutils/with-test-logging (assert-no-autosign whitelist invalid-line) - (is (logutils/logs-matching + (is (logged? (re-pattern (format "Invalid pattern '%s' found in %s" - invalid-line whitelist)) - @logs)) + invalid-line whitelist)))) (assert-autosign whitelist "qux"))))) (testing "sample file that covers everything" @@ -360,34 +370,34 @@ (testing "stdout is added to master's log at debug level" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) + (ca/autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) (is (logged? #"print to stdout" :debug)))) (testing "stderr is added to master's log at warn level" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) + (ca/autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) (is (logged? #"generated output to stderr: print to stderr" :warn)))) (testing "non-zero exit-code generates a log entry at warn level" (logutils/with-test-logging - (autosign-csr? executable "foo" (csr-fn) ruby-load-path ruby-gem-path) + (ca/autosign-csr? executable "foo" (csr-fn) ruby-load-path ruby-gem-path) (is (logged? #"rejected certificate 'foo'" :warn)))) (testing "Ruby load path is configured and contains Puppet" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) + (ca/autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) (is (logged? #"Ruby load path configured properly")))) (testing "subject is passed as argument and CSR is provided on stdin" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) + (ca/autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path) (is (logged? #"subject: test-agent")) (is (logged? #"CSR for: test-agent")))) (testing "only exit code 0 results in autosigning" (logutils/with-test-logging - (is (true? (autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path))) - (is (false? (autosign-csr? executable "foo" (csr-fn) ruby-load-path ruby-gem-path))))))) + (is (true? (ca/autosign-csr? executable "test-agent" (csr-fn) ruby-load-path ruby-gem-path))) + (is (false? (ca/autosign-csr? executable "foo" (csr-fn) ruby-load-path ruby-gem-path))))))) (deftest autosign-csr?-bash-exe-test (let [executable (autosign-exe-file "bash-autosign-executable") @@ -395,40 +405,40 @@ (testing "stdout is added to master's log at debug level" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn)) + (ca/autosign-csr? executable "test-agent" (csr-fn)) (is (logged? #"print to stdout" :debug)))) (testing "stderr is added to master's log at warn level" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn)) + (ca/autosign-csr? executable "test-agent" (csr-fn)) (is (logged? #"generated output to stderr: print to stderr" :warn)))) (testing "non-zero exit-code generates a log entry at warn level" (logutils/with-test-logging - (autosign-csr? executable "foo" (csr-fn)) + (ca/autosign-csr? executable "foo" (csr-fn)) (is (logged? #"rejected certificate 'foo'" :warn)))) (testing "subject is passed as argument and CSR is provided on stdin" (logutils/with-test-logging - (autosign-csr? executable "test-agent" (csr-fn)) + (ca/autosign-csr? executable "test-agent" (csr-fn)) (is (logged? #"subject: test-agent")) (is (logged? #"-----BEGIN CERTIFICATE REQUEST-----")))) (testing "only exit code 0 results in autosigning" (logutils/with-test-logging - (is (true? (autosign-csr? executable "test-agent" (csr-fn)))) - (is (false? (autosign-csr? executable "foo" (csr-fn)))))))) + (is (true? (ca/autosign-csr? executable "test-agent" (csr-fn)))) + (is (false? (ca/autosign-csr? executable "foo" (csr-fn)))))))) (deftest save-certificate-request!-test (testing "requests are saved to disk" (let [csrdir (:csrdir (testutils/ca-sandbox! cadir)) - csr (utils/pem->csr (path-to-cert-request csrdir "test-agent")) - path (path-to-cert-request csrdir "foo")] + csr (utils/pem->csr (ca/path-to-cert-request csrdir "test-agent")) + path (ca/path-to-cert-request csrdir "foo")] (is (false? (fs/exists? path))) - (save-certificate-request! "foo" csr csrdir) + (ca/save-certificate-request! "foo" csr csrdir) (is (true? (fs/exists? path))) - (is (= (get-certificate-request csrdir "foo") - (get-certificate-request csrdir "test-agent")))))) + (is (= (ca/get-certificate-request csrdir "foo") + (ca/get-certificate-request csrdir "test-agent")))))) (deftest autosign-certificate-request!-test (let [now (time/epoch) @@ -436,12 +446,12 @@ settings (-> (testutils/ca-sandbox! cadir) (assoc :ca-ttl two-years)) csr (-> (:csrdir settings) - (path-to-cert-request "test-agent") + (ca/path-to-cert-request "test-agent") (utils/pem->csr)) - expected-cert-path (path-to-cert (:signeddir settings) "test-agent")] + expected-cert-path (ca/path-to-cert (:signeddir settings) "test-agent")] ;; Fix the value of "now" so we can reliably test the dates (time/do-at now - (autosign-certificate-request! "test-agent" csr settings)) + (ca/autosign-certificate-request! "test-agent" csr settings (constantly nil))) (testing "requests are autosigned and saved to disk" (is (fs/exists? expected-cert-path))) @@ -465,11 +475,11 @@ (testing "The CA public key file is not necessary to autosign" (let [settings (testutils/ca-sandbox! cadir) csr (-> (:csrdir settings) - (path-to-cert-request "test-agent") + (ca/path-to-cert-request "test-agent") (utils/pem->csr)) - cert-path (path-to-cert (:signeddir settings) "test-agent")] + cert-path (ca/path-to-cert (:signeddir settings) "test-agent")] (fs/delete (:capub settings)) - (autosign-certificate-request! "test-agent" csr settings) + (ca/autosign-certificate-request! "test-agent" csr settings (constantly nil)) (is (true? (fs/exists? cert-path))) (let [cert (utils/pem->cert cert-path) capub (-> (:cacert settings) @@ -481,10 +491,10 @@ (testing "The CA certificate file can be a bundle of certs" (let [settings (testutils/ca-sandbox! bundle-cadir) csr (-> (:csrdir settings) - (path-to-cert-request "test-agent") + (ca/path-to-cert-request "test-agent") (utils/pem->csr)) - cert-path (path-to-cert (:signeddir settings) "test-agent")] - (autosign-certificate-request! "test-agent" csr settings) + cert-path (ca/path-to-cert (:signeddir settings) "test-agent")] + (ca/autosign-certificate-request! "test-agent" csr settings (constantly nil)) (is (true? (fs/exists? cert-path))) (let [cert (utils/pem->cert cert-path) capub (-> (:cacert settings) @@ -497,7 +507,7 @@ (testing "The CA public key file is not necessary to revoke" (let [settings (testutils/ca-sandbox! cadir) cert (-> (:signeddir settings) - (path-to-cert "localhost") + (ca/path-to-cert "localhost") (utils/pem->cert)) revoked? (fn [cert] (-> (:cacrl settings) @@ -505,14 +515,14 @@ (utils/revoked? cert)))] (fs/delete (:capub settings)) (is (false? (revoked? cert))) - (revoke-existing-certs! settings ["localhost"]) + (ca/revoke-existing-certs! settings ["localhost"] (constantly nil)) (is (true? (revoked? cert)))))) (deftest revoke-as-intermediate-ca (testing "The CA certificate file can be a bundle when revoking a certificate" (let [settings (testutils/ca-sandbox! bundle-cadir) cert (-> (:signeddir settings) - (path-to-cert "localhost") + (ca/path-to-cert "localhost") (utils/pem->cert)) ca-cert (utils/pem->ca-cert (:cacert settings) (:cakey settings)) revoked? (fn [cert] @@ -520,17 +530,17 @@ (utils/pem->ca-crl ca-cert) (utils/revoked? cert)))] (is (false? (revoked? cert))) - (revoke-existing-certs! settings ["localhost"]) + (ca/revoke-existing-certs! settings ["localhost"] (constantly nil)) (is (true? (revoked? cert)))))) (deftest revoke-multiple-certs (testing "The revocation function can accept a list of certs to revoke" (let [settings (testutils/ca-sandbox! cadir) cert1 (-> (:signeddir settings) - (path-to-cert "localhost") + (ca/path-to-cert "localhost") (utils/pem->cert)) cert2 (-> (:signeddir settings) - (path-to-cert "test_cert") + (ca/path-to-cert "test_cert") (utils/pem->cert)) revoked? (fn [cert] (-> (:cacrl settings) @@ -538,32 +548,42 @@ (utils/revoked? cert)))] (is (false? (revoked? cert1))) (is (false? (revoked? cert2))) - (revoke-existing-certs! settings ["localhost" "test_cert"]) + (ca/revoke-existing-certs! settings ["localhost" "test_cert"] (constantly nil)) (is (true? (revoked? cert1))) (is (true? (revoked? cert2)))))) +(defn make-big-integers + [integers] + (map biginteger integers)) + (deftest filter-already-revoked-serials-test - (let [crl (-> (get-certificate-revocation-list cacrl) + (let [lock (new ReentrantReadWriteLock) + descriptor "test-crl" + timeout 1 + crl (-> (ca/get-certificate-revocation-list cacrl lock descriptor timeout) StringReader. utils/pem->crl)] (testing "Return an empty vector when all supplied serials are already in CRL" - (let [test-serial (vec [4]) - filtered-serial (filter-already-revoked-serials test-serial crl)] + (let [test-serial (make-big-integers [(biginteger 4)]) + filtered-serial (ca/filter-already-revoked-serials test-serial crl)] (is (empty? filtered-serial)))) (testing "Return a vector of serials not yet in CRL" - (let [test-serial (vec [1 2 3 4]) - filtered-serial (filter-already-revoked-serials test-serial crl)] + (let [test-serial (make-big-integers [1 2 3 4]) + filtered-serial (ca/filter-already-revoked-serials test-serial crl)] (is (true? (= (sort filtered-serial) [1 2 3]))))) (testing "Deduplicates the vector of serials to be revoked" - (let [test-serial (vec [1 1 2 2 3 3]) - filtered-serial (filter-already-revoked-serials test-serial crl)] + (let [test-serial (make-big-integers [1 1 2 2 3 3]) + filtered-serial (ca/filter-already-revoked-serials test-serial crl)] (is (apply distinct? filtered-serial)))))) (deftest get-certificate-revocation-list-test (testing "`get-certificate-revocation-list` returns a valid CRL file." - (let [crl (-> (get-certificate-revocation-list cacrl) + (let [lock (new ReentrantReadWriteLock) + descriptor "test-crl" + timeout 1 + crl (-> (ca/get-certificate-revocation-list cacrl lock descriptor timeout) StringReader. utils/pem->crl)] (testutils/assert-issuer crl "CN=Puppet CA: localhost")))) @@ -578,7 +598,7 @@ (let [new-crl-path (str update-crl-fixture-dir "new_root_crl.pem") incoming-crls (utils/pem->crls new-crl-path)] (testutils/with-backed-up-crl crl-path crl-backup-path - (update-crls incoming-crls crl-path cert-chain-path) + (ca/update-crls incoming-crls crl-path cert-chain-path) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls crl-path) old-number (utils/get-crl-number (last old-crls)) @@ -588,7 +608,7 @@ (let [multiple-new-crls-path (str update-crl-fixture-dir "multiple_new_root_crls.pem") incoming-crls (utils/pem->crls multiple-new-crls-path)] (testutils/with-backed-up-crl crl-path crl-backup-path - (update-crls incoming-crls crl-path cert-chain-path) + (ca/update-crls incoming-crls crl-path cert-chain-path) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls crl-path) old-number (utils/get-crl-number (last old-crls)) @@ -601,7 +621,7 @@ three-newer-crls-path (str update-crl-fixture-dir "three_newer_crl_chain.pem") incoming-crls (utils/pem->crls three-newer-crls-path)] (testutils/with-backed-up-crl three-crl-path crl-backup-path - (update-crls incoming-crls three-crl-path three-cert-path) + (ca/update-crls incoming-crls three-crl-path three-cert-path) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls three-crl-path)] (is (> (utils/get-crl-number (.get new-crls 2)) @@ -615,7 +635,7 @@ (let [new-and-unrelated-crls-path (str update-crl-fixture-dir "new_crls_and_unrelated_crls.pem") incoming-crls (utils/pem->crls new-and-unrelated-crls-path)] (testutils/with-backed-up-crl crl-path crl-backup-path - (update-crls incoming-crls crl-path cert-chain-path) + (ca/update-crls incoming-crls crl-path cert-chain-path) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls crl-path)] (is (> (utils/get-crl-number (last new-crls)) @@ -632,7 +652,7 @@ (let [unrelated-crls-path (str update-crl-fixture-dir "unrelated_crls.pem") incoming-crls (utils/pem->crls unrelated-crls-path)] (testutils/with-backed-up-crl crl-path crl-backup-path - (update-crls incoming-crls crl-path cert-chain-path) + (ca/update-crls incoming-crls crl-path cert-chain-path) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls crl-path)] (is (= (count old-crls) (count new-crls))) @@ -642,10 +662,11 @@ old-root-crl-path (str update-crl-fixture-dir "old_root_crl.pem") incoming-crls (utils/pem->crls old-root-crl-path)] (testutils/with-backed-up-crl new-root-crl-chain-path crl-backup-path - (update-crls incoming-crls new-root-crl-chain-path cert-chain-path) + (ca/update-crls incoming-crls new-root-crl-chain-path cert-chain-path) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls new-root-crl-chain-path)] (is (= (set new-crls) (set old-crls))))))) + (let [multiple-newest-crls-path (str update-crl-fixture-dir "multiple_newest_root_crls.pem") delta-crl-path (str test-resources-dir "/update_crls/delta_crl.pem") missing-auth-id-crl-path (str test-resources-dir "/update_crls/missing_auth_id_crl.pem") @@ -657,7 +678,7 @@ (let [incoming-crls (utils/pem->crls path)] (testutils/with-backed-up-crl crl-path crl-backup-path (is (thrown-with-msg? IllegalArgumentException error-message - (update-crls incoming-crls crl-path cert-chain-path))) + (ca/update-crls incoming-crls crl-path cert-chain-path))) (let [old-crls (utils/pem->crls crl-backup-path) new-crls (utils/pem->crls crl-path)] (is (= (set old-crls) (set new-crls)))))))))))) @@ -665,11 +686,11 @@ (deftest initialize!-test (let [settings (testutils/ca-settings (ks/temp-dir))] - (initialize! settings) + (ca/initialize! settings) (testing "Generated SSL file" - (doseq [file (-> (settings->cadir-paths settings) - (select-keys (required-ca-files + (doseq [file (-> (ca/settings->cadir-paths settings) + (select-keys (ca/required-ca-files (:enable-infra-crl settings))) (vals))] (testing file @@ -691,7 +712,7 @@ (let [key-usage (utils/get-extension-value cert "2.5.29.15")] (is (= #{:key-cert-sign :crl-sign} key-usage)))) (testing "does not have any SANs" - (is (nil? (utils/get-extension-value cert subject-alt-names-oid)))) + (is (nil? (utils/get-extension-value cert ca/subject-alt-names-oid)))) (testing "authority key identifier is SHA of public key" (let [ca-pub-key (-> settings :capub utils/pem->public-key) pub-key-sha (pubkey-sha1 ca-pub-key)] @@ -714,18 +735,24 @@ (testing "serial" (is (fs/exists? (:serial settings)))) + (testing "allow-auto-renewal" + (is (= false (:allow-auto-renewal settings)))) + + (testing "auto-renewal-cert-ttl" + (is (= "90d" (:auto-renewal-cert-ttl settings)))) + (testing "Does not replace files if they all exist" - (let [files (-> (settings->cadir-paths (assoc settings :enable-infra-crl false)) + (let [files (-> (ca/settings->cadir-paths (assoc settings :enable-infra-crl false)) (dissoc :csrdir :signeddir :cadir) (vals))] (doseq [f files] (spit f "testable string")) - (initialize! settings) + (ca/initialize! settings) (doseq [f files] (is (= "testable string" (slurp f)) (str "File " f " was replaced"))))))) (deftest initialize!-test-with-keylength-in-settings (let [settings (assoc (testutils/ca-settings (ks/temp-dir)) :keylength 768)] - (initialize! settings) + (ca/initialize! settings) (testing "cakey with keylength" (let [key (-> settings :cakey utils/pem->private-key)] (is (utils/private-key? key)) @@ -742,16 +769,16 @@ (testing dir (let [settings (testutils/ca-sandbox! cadir)] (fs/delete-dir (get settings dir)) - (is (nil? (initialize! settings))) + (is (nil? (ca/initialize! settings))) (is (true? (fs/exists? (get settings dir)))))))) (testing "CA public key not required" (let [settings (testutils/ca-sandbox! cadir)] (fs/delete (:capub settings)) - (is (nil? (initialize! settings))))) + (is (nil? (ca/initialize! settings))))) (testing "Exception is thrown when required file is missing" - (doseq [file (required-ca-files true)] + (doseq [file (ca/required-ca-files true)] (testing file (let [settings (assoc (testutils/ca-sandbox! cadir) :enable-infra-crl true) path (get settings file)] @@ -759,25 +786,23 @@ (is (thrown-with-msg? IllegalStateException (re-pattern (str "Missing:\n" path)) - (initialize! settings))))))) + (ca/initialize! settings))))))) - (testing (str "The CA private key has its permissions properly reset when " - ":manage-internal-file-permissions is true.") + (testing "The CA private key has its permissions properly reset when :manage-internal-file-permissions is true." (let [settings (testutils/ca-sandbox! cadir)] - (set-file-perms (:cakey settings) "rw-r--r--") + (ks-file/set-perms (:cakey settings) "rw-r--r--") (logutils/with-test-logging - (initialize! settings) + (ca/initialize! settings) (is (logged? #"/ca/ca_key.pem' was found to have the wrong permissions set as 'rw-r--r--'. This has been corrected to 'rw-r-----'.")) - (is (= private-key-perms (get-file-perms (:cakey settings))))))) + (is (= ca/private-key-perms (ks-file/get-perms (:cakey settings))))))) - (testing (str "The CA private key's permissions are not reset if " - ":manage-internal-file-permissions is false.") + (testing "The CA private key's permissions are not reset if :manage-internal-file-permissions is false." (let [perms "rw-r--r--" settings (assoc (testutils/ca-sandbox! cadir) :manage-internal-file-permissions false)] - (set-file-perms (:cakey settings) perms) - (initialize! settings) - (is (= perms (get-file-perms (:cakey settings))))))) + (ks-file/set-perms (:cakey settings) perms) + (ca/initialize! settings) + (is (= perms (ks-file/get-perms (:cakey settings))))))) (deftest retrieve-ca-cert!-test (testing "CA file copied when it doesn't already exist" @@ -789,14 +814,14 @@ cacert-text (slurp cacert)] (testing "Copied cacert to localcacert when localcacert not present" - (retrieve-ca-cert! cacert localcacert) + (ca/retrieve-ca-cert! cacert localcacert) (is (= (slurp localcacert) cacert-text) (str "Unexpected content for localcacert: " localcacert))) (testing "Doesn't copy cacert over localcacert when different localcacert present" (let [localcacert-contents "12345"] (spit (:localcacert settings) localcacert-contents) - (retrieve-ca-cert! cacert localcacert) + (ca/retrieve-ca-cert! cacert localcacert) (is (= (slurp localcacert) localcacert-contents) (str "Unexpected content for localcacert: " localcacert)))) @@ -805,7 +830,7 @@ (let [copy (fs/copy cacert (ks/temp-file))] (fs/delete cacert) (is (thrown? IllegalStateException - (retrieve-ca-cert! cacert localcacert)) + (ca/retrieve-ca-cert! cacert localcacert)) "No exception thrown even though no file existed for copying") (fs/copy copy cacert)))))) @@ -819,18 +844,17 @@ cacrl-text (slurp cacrl)] (testing "Copied cacrl to hostcrl when hostcrl not present" - (retrieve-ca-crl! cacrl hostcrl) + (ca/retrieve-ca-crl! cacrl hostcrl) (is (= (slurp hostcrl) cacrl-text) (str "Unexpected content for hostcrl: " hostcrl))) (testing "Copied cacrl to hostcrl when different hostcrl present" (spit (:hostcrl settings) "12345") - (retrieve-ca-crl! cacrl hostcrl) + (ca/retrieve-ca-crl! cacrl hostcrl) (is (= (slurp hostcrl) cacrl-text) (str "Unexpected content for hostcrl: " hostcrl))) - (testing (str "Doesn't throw exception or create dummy file if no " - "hostcrl and no cacrl to copy") + (testing "Doesn't throw exception or create dummy file if no hostcrl and no cacrl to copy" (fs/delete hostcrl) (let [copy (fs/copy cacrl (ks/temp-file))] (fs/delete cacrl) @@ -844,12 +868,12 @@ (assoc :dns-alt-names "onefish,twofish")) ca-settings (testutils/ca-settings (str tmp-confdir "/ca"))] - (retrieve-ca-cert! (:cacert ca-settings) (:localcacert settings)) - (retrieve-ca-crl! (:cacrl ca-settings) (:hostcrl settings)) - (initialize-master-ssl! settings "master" ca-settings) + (ca/retrieve-ca-cert! (:cacert ca-settings) (:localcacert settings)) + (ca/retrieve-ca-crl! (:cacrl ca-settings) (:hostcrl settings)) + (ca/initialize-master-ssl! settings "master" ca-settings) (testing "Generated SSL file" - (doseq [file (vals (settings->ssldir-paths settings))] + (doseq [file (vals (ca/settings->ssldir-paths settings))] (testing file (is (fs/exists? file))))) @@ -866,12 +890,12 @@ master's actual hostname and the hostnames in $dns-alt-names"))) (testing "has CLI auth extension" - (let [cli-auth-ext (utils/get-extension-value hostcert cli-auth-oid)] + (let [cli-auth-ext (utils/get-extension-value hostcert ca/cli-auth-oid)] (is (= "true" cli-auth-ext) "The master cert should have the auth extension for the CA CLI."))) (testing "is also saved in the CA's $signeddir" - (let [signedpath (path-to-cert (:signeddir ca-settings) "master")] + (let [signedpath (ca/path-to-cert (:signeddir ca-settings) "master")] (is (fs/exists? signedpath)) (is (= hostcert (utils/pem->cert signedpath))))))) @@ -886,7 +910,7 @@ (is (= 512 (utils/keylength key))))) (testing "Does not replace files if they all exist" - (let [files (-> (settings->ssldir-paths settings) + (let [files (-> (ca/settings->ssldir-paths settings) (dissoc :certdir :requestdir :privatekeydir) (vals)) file-content-fn (fn [files-to-read] @@ -895,7 +919,7 @@ {} files-to-read)) file-content-before-reinit (file-content-fn files) - _ (initialize-master-ssl! settings "master" ca-settings) + _ (ca/initialize-master-ssl! settings "master" ca-settings) file-content-after-reinit (file-content-fn files)] (is (= file-content-before-reinit file-content-after-reinit) "File content unexpectedly changed after initialization called"))) @@ -910,11 +934,10 @@ (str "Found master cert '" (:hostcert settings) "' but master private key '" (:hostprivkey settings) "' is missing")) - (initialize-master-ssl! settings "master" ca-settings))) + (ca/initialize-master-ssl! settings "master" ca-settings))) (fs/copy private-key-backup private-key-path))) - (testing (str "Throws an exception if the private key is present but cert " - "and public key are missing") + (testing "Throws an exception if the private key is present but cert and public key are missing" (let [public-key-path (:hostpubkey settings) public-key-backup (fs/copy public-key-path (ks/temp-file)) cert-path (:hostcert settings) @@ -927,12 +950,11 @@ (str "Found master private key '" (:hostprivkey settings) "' but master public key '" (:hostpubkey settings) "' is missing")) - (initialize-master-ssl! settings "master" ca-settings))) + (ca/initialize-master-ssl! settings "master" ca-settings))) (fs/copy public-key-backup public-key-path) (fs/copy cert-backup cert-path))) - (testing (str "Throws an exception if the public key is present but cert " - "and private key are missing") + (testing "Throws an exception if the public key is present but cert and private key are missing" (let [private-key-path (:hostprivkey settings) private-key-backup (fs/copy private-key-path (ks/temp-file)) cert-path (:hostcert settings) @@ -945,7 +967,7 @@ (str "Found master public key '" (:hostpubkey settings) "' but master private key '" (:hostprivkey settings) "' is missing")) - (initialize-master-ssl! settings "master" ca-settings))) + (ca/initialize-master-ssl! settings "master" ca-settings))) (fs/copy private-key-backup private-key-path) (fs/copy cert-backup cert-path))) @@ -954,7 +976,7 @@ hostcert-backup (fs/copy hostcert-path (ks/temp-file)) public-key-before-init (slurp (:hostpubkey settings)) _ (fs/delete hostcert-path) - _ (initialize-master-ssl! settings "master" ca-settings) + _ (ca/initialize-master-ssl! settings "master" ca-settings) hostcert-after-init (utils/pem->cert hostcert-path) public-key-from-new-cert (StringWriter.)] (-> hostcert-after-init @@ -972,8 +994,8 @@ (assoc :keylength 768)) ca-settings (assoc (testutils/ca-settings (str tmp-confdir "/ca")) :keylength 768)] - (retrieve-ca-cert! (:cacert ca-settings) (:localcacert settings)) - (initialize-master-ssl! settings "master" ca-settings) + (ca/retrieve-ca-cert! (:cacert ca-settings) (:localcacert settings)) + (ca/initialize-master-ssl! settings "master" ca-settings) (testing "hostprivkey should have correct keylength" (let [key (-> settings :hostprivkey utils/pem->private-key)] @@ -991,42 +1013,44 @@ settings (testutils/master-settings tmp-confdir) ca-settings (testutils/ca-settings (str tmp-confdir "/ca"))] - (retrieve-ca-cert! (:cacert ca-settings) (:localcacert settings)) + (ca/retrieve-ca-cert! (:cacert ca-settings) (:localcacert settings)) (testing "should throw an error message with too short keylength" (is (thrown? IllegalArgumentException - (initialize-master-ssl! (assoc settings :keylength 128) "master" ca-settings)))) + (ca/initialize-master-ssl! (assoc settings :keylength 128) "master" ca-settings)))) (testing "should throw an error message with too large keylength" (is (thrown? IllegalArgumentException - (initialize-master-ssl! (assoc settings :keylength 32768) "master" ca-settings))))))) + (ca/initialize-master-ssl! (assoc settings :keylength 32768) "master" ca-settings))))))) (deftest parse-serial-number-test - (is (= (parse-serial-number "0001") 1)) - (is (= (parse-serial-number "0010") 16)) - (is (= (parse-serial-number "002A") 42))) + (is (= (ca/parse-serial-number "0001") 1)) + (is (= (ca/parse-serial-number "0010") 16)) + (is (= (ca/parse-serial-number "002A") 42))) (deftest format-serial-number-test - (is (= (format-serial-number 1) "0001")) - (is (= (format-serial-number 16) "0010")) - (is (= (format-serial-number 42) "002A"))) + (is (= (ca/format-serial-number 1) "0001")) + (is (= (ca/format-serial-number 16) "0010")) + (is (= (ca/format-serial-number 42) "002A"))) (deftest next-serial-number!-test - (let [serial-file (str (ks/temp-file))] + (let [serial-file (str (ks/temp-file)) + ca-settings (assoc (testutils/ca-settings (ks/temp-dir)) + :serial serial-file)] (testing "Serial file is initialized to 1" - (initialize-serial-file! serial-file) - (is (= (next-serial-number! serial-file) 1))) + (ca/initialize-serial-file! ca-settings) + (is (= (ca/next-serial-number! ca-settings) 1))) (testing "The serial number file should contain the next serial number" (is (= "0002" (slurp serial-file)))) (testing "subsequent calls produce increasing serial numbers" - (is (= (next-serial-number! serial-file) 2)) + (is (= (ca/next-serial-number! ca-settings) 2)) (is (= "0003" (slurp serial-file))) - (is (= (next-serial-number! serial-file) 3)) + (is (= (ca/next-serial-number! ca-settings) 3)) (is (= "0004" (slurp serial-file)))))) ;; If the locking is deleted from `next-serial-number!`, this test will hang, @@ -1038,14 +1062,15 @@ never returns a duplicate serial number" (let [serial-file (doto (str (ks/temp-file)) (spit "0001")) serials (atom []) - + ca-settings (assoc (testutils/ca-settings (ks/temp-dir)) + :serial serial-file) ;; spin off a new thread for each CPU promises (for [_ (range (ks/num-cpus))] (let [p (promise)] (future ;; get a bunch of serial numbers and keep track of them (dotimes [_ 100] - (let [serial-number (next-serial-number! serial-file)] + (let [serial-number (ca/next-serial-number! ca-settings)] (swap! serials conj serial-number))) (deliver p 'done)) p)) @@ -1056,7 +1081,31 @@ (doseq [p promises] (deref p)) (is (false? (contains-duplicates? @serials)) - "Got a duplicate serial number")))) + "Got a duplicate serial number"))) + (testing "next-serial-number! will timeout if lock is held" + (let + [serial-file (str (ks/temp-file)) + ca-settings (assoc (testutils/ca-settings (ks/temp-dir)) + :serial serial-file + :serial-lock-timeout-seconds 1) + write-lock (.writeLock (:serial-lock ca-settings)) + completed (promise) + caught-timeout (promise)] + ;; acquire and hold the lock + (.lock write-lock) + ;; in a separate thread, try to increment the serial number + (deref + (future + (try + (ca/next-serial-number! ca-settings) + ;; we should never get here because the lock is held, and timeout should occur + (deliver completed true) + (catch Exception _e + ;; timeout occurred + (deliver caught-timeout false))))) + (.unlock write-lock) + (is (not (realized? completed))) + (is (realized? caught-timeout))))) (defn verify-inventory-entry! [inventory-entry serial-number not-before not-after subject] @@ -1069,10 +1118,13 @@ (deftest test-write-cert-to-inventory (testing "Certs can be written to an inventory file." (let [first-cert (utils/pem->cert cacert) - second-cert (utils/pem->cert (path-to-cert signeddir "localhost")) - inventory-file (str (ks/temp-file))] - (write-cert-to-inventory! first-cert inventory-file) - (write-cert-to-inventory! second-cert inventory-file) + second-cert (utils/pem->cert (ca/path-to-cert signeddir "localhost")) + inventory-file (str (ks/temp-file)) + ca-settings (assoc + (testutils/ca-settings cadir) + :cert-inventory inventory-file)] + (ca/write-cert-to-inventory! first-cert ca-settings) + (ca/write-cert-to-inventory! second-cert ca-settings) (testing "The format of a cert in the inventory matches the existing format used by the ruby puppet code." @@ -1102,41 +1154,43 @@ (is (thrown+? [:kind :duplicate-cert :msg "test-agent already has a requested certificate; ignoring certificate request"] - (process-csr-submission! "test-agent" (csr-stream "test-agent") settings)))) + (ca/process-csr-submission! "test-agent" (csr-stream "test-agent") settings (constantly nil))))) (testing "throws exception if certificate already exists" (is (thrown+? [:kind :duplicate-cert :msg "localhost already has a signed certificate; ignoring certificate request"] - (process-csr-submission! "localhost" + (ca/process-csr-submission! "localhost" (io/input-stream (test-pem-file "localhost-csr.pem")) - settings))) + settings + (constantly nil)))) (is (thrown+? [:kind :duplicate-cert :msg "revoked-agent already has a revoked certificate; ignoring certificate request"] - (process-csr-submission! "revoked-agent" + (ca/process-csr-submission! "revoked-agent" (io/input-stream (test-pem-file "revoked-agent-csr.pem")) - settings)))))) + settings + (constantly nil))))))) (testing "when true" (let [settings (assoc settings :allow-duplicate-certs true)] (testing "new CSR overwrites existing one" - (let [csr-path (path-to-cert-request (:csrdir settings) "test-agent") + (let [csr-path (ca/path-to-cert-request (:csrdir settings) "test-agent") csr (ByteArrayInputStream. (.getBytes (slurp csr-path)))] (spit csr-path "should be overwritten") (logutils/with-test-logging - (process-csr-submission! "test-agent" csr settings) + (ca/process-csr-submission! "test-agent" csr settings (constantly nil)) (is (logged? #"test-agent already has a requested certificate; new certificate will overwrite it" :info)) (is (not= "should be overwritten" (slurp csr-path)) "Existing CSR was not overwritten")))) (testing "new certificate overwrites existing one" (let [settings (assoc settings :autosign true) - cert-path (path-to-cert (:signeddir settings) "localhost") + cert-path (ca/path-to-cert (:signeddir settings) "localhost") old-cert (slurp cert-path) csr (io/input-stream (test-pem-file "localhost-csr.pem"))] (logutils/with-test-logging - (process-csr-submission! "localhost" csr settings) + (ca/process-csr-submission! "localhost" csr settings (constantly nil)) (is (logged? #"localhost already has a signed certificate; new certificate will overwrite it" :info)) (is (not= old-cert (slurp cert-path)) "Existing certificate was not overwritten")))))))) @@ -1160,10 +1214,10 @@ :msg "Subject contains a wildcard, which is not allowed: foo*bar"} (select-keys % [:kind :msg]))]]] (testing policy - (let [path (path-to-cert-request (:csrdir settings) subject) + (let [path (ca/path-to-cert-request (:csrdir settings) subject) csr (io/input-stream (test-pem-file csr-file))] (is (false? (fs/exists? path))) - (is (thrown+? exception (process-csr-submission! subject csr settings))) + (is (thrown+? exception (ca/process-csr-submission! subject csr settings (constantly nil)))) (is (false? (fs/exists? path))))))) (testing "extension & key policies are not checked" @@ -1172,12 +1226,20 @@ ["unknown extension" "meow" "meow-bad-extension.pem"] ["public-private key mismatch" "luke.madstop.com" "luke.madstop.com-bad-public-key.pem"]]] (testing policy - (let [path (path-to-cert-request (:csrdir settings) subject) + (let [path (ca/path-to-cert-request (:csrdir settings) subject) csr (io/input-stream (test-pem-file csr-file))] (is (false? (fs/exists? path))) - (process-csr-submission! subject csr settings) + (ca/process-csr-submission! subject csr settings (constantly nil)) (is (true? (fs/exists? path))) - (fs/delete path))))))) + (fs/delete path))))) + (testing "writes indicator file when asked to" + (let [subject "meow" + path (ca/path-to-cert-request (:csrdir settings) subject) + csr (io/input-stream (test-pem-file "meow-bad-extension.pem"))] + (is (false? (fs/exists? path))) + (ca/process-csr-submission! subject csr settings (constantly nil)) + (is (true? (fs/exists? path))) + (fs/delete path))))) (testing "when autosign is true, all policies are checked, and" (let [settings (assoc settings :autosign true)] @@ -1196,10 +1258,10 @@ :msg "Subject contains a wildcard, which is not allowed: foo*bar"} (select-keys % [:kind :msg]))]]] (testing policy - (let [path (path-to-cert-request (:csrdir settings) subject) + (let [path (ca/path-to-cert-request (:csrdir settings) subject) csr (io/input-stream (test-pem-file csr-file))] (is (false? (fs/exists? path))) - (is (thrown+? expected (process-csr-submission! subject csr settings))) + (is (thrown+? expected (ca/process-csr-submission! subject csr settings (constantly nil)))) (is (false? (fs/exists? path))))))) (testing "CSR will be saved when" @@ -1221,10 +1283,10 @@ :msg "CSR contains a public key that does not correspond to the signing key"} (select-keys % [:kind :msg]))]]] (testing policy - (let [path (path-to-cert-request (:csrdir settings) subject) + (let [path (ca/path-to-cert-request (:csrdir settings) subject) csr (io/input-stream (test-pem-file csr-file))] (is (false? (fs/exists? path))) - (is (thrown+? expected (process-csr-submission! subject csr settings))) + (is (thrown+? expected (ca/process-csr-submission! subject csr settings (constantly nil)))) (is (true? (fs/exists? path))) (fs/delete path))))))) @@ -1235,13 +1297,13 @@ (is (thrown+? [:kind :duplicate-cert :msg "test-agent already has a requested certificate; ignoring certificate request"] - (process-csr-submission! "not-test-agent" csr-with-mismatched-name settings))))) + (ca/process-csr-submission! "not-test-agent" csr-with-mismatched-name settings (constantly nil)))))) (testing "subject policies checked before extension & key policies" (let [csr-with-disallowed-alt-names (io/input-stream (test-pem-file "hostwithaltnames.pem"))] (is (thrown+? [:kind :hostname-mismatch :msg "Instance name \"hostwithaltnames\" does not match requested key \"foo\""] - (process-csr-submission! "foo" csr-with-disallowed-alt-names settings))))))))) + (ca/process-csr-submission! "foo" csr-with-disallowed-alt-names settings (constantly nil)))))))))) (deftest cert-signing-extension-test (let [issuer-keys (utils/generate-key-pair 512) @@ -1263,10 +1325,10 @@ (testing "basic extensions are created for an agent" (let [csr (utils/generate-certificate-request subject-keys subject-dn) - exts (create-agent-extensions csr cert) + exts (ca/create-agent-extensions csr cert) exts-expected [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.17" :critical false :value {:dns-name [subject]}} @@ -1281,7 +1343,7 @@ :value {:is-ca false}} {:oid "2.5.29.37" :critical true - :value [ssl-server-cert ssl-client-cert]} + :value [ca/ssl-server-cert ca/ssl-client-cert]} {:oid "2.5.29.15" :critical true :value #{:digital-signature :key-encipherment}} @@ -1296,10 +1358,10 @@ subject-keys subject-dn [(utils/subject-dns-alt-names alt-names-list false)]) - exts (create-agent-extensions csr cert) + exts (ca/create-agent-extensions csr cert) exts-expected [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.17" :critical false :value {:dns-name alt-names-list}} @@ -1314,7 +1376,7 @@ :value {:is-ca false}} {:oid "2.5.29.37" :critical true - :value [ssl-server-cert ssl-client-cert]} + :value [ca/ssl-server-cert ca/ssl-client-cert]} {:oid "2.5.29.15" :critical true :value #{:digital-signature :key-encipherment}} @@ -1329,10 +1391,10 @@ subject-keys subject-dn [(utils/subject-dns-alt-names alt-names-list false)]) - exts (create-agent-extensions csr cert) + exts (ca/create-agent-extensions csr cert) exts-expected [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.17" :critical false :value {:dns-name (conj alt-names-list subject)}} @@ -1347,7 +1409,7 @@ :value {:is-ca false}} {:oid "2.5.29.37" :critical true - :value [ssl-server-cert ssl-client-cert]} + :value [ca/ssl-server-cert ca/ssl-client-cert]} {:oid "2.5.29.15" :critical true :value #{:digital-signature :key-encipherment}} @@ -1359,13 +1421,13 @@ (testing "basic extensions are created for a master" (let [settings (assoc (testutils/master-settings confdir) :csr-attributes "doesntexist") - exts (create-master-extensions subject - subject-pub - cert - settings) + exts (ca/create-master-extensions subject + subject-pub + cert + settings) exts-expected [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.35" :critical false :value {:issuer-dn nil @@ -1377,7 +1439,7 @@ :value {:is-ca false}} {:oid "2.5.29.37" :critical true - :value [ssl-server-cert ssl-client-cert]} + :value [ca/ssl-server-cert ca/ssl-client-cert]} {:oid "2.5.29.15" :critical true :value #{:digital-signature :key-encipherment}} @@ -1397,14 +1459,14 @@ settings (-> (testutils/master-settings confdir) (assoc :dns-alt-names dns-alt-names) (assoc :csr-attributes (csr-attributes-file "csr_attributes.yaml"))) - exts (create-master-extensions subject - subject-pub - cert - settings) + exts (ca/create-master-extensions subject + subject-pub + cert + settings) exts-expected (concat attribute-file-extensions [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.35" :critical false :value {:issuer-dn nil @@ -1416,7 +1478,7 @@ :value {:is-ca false}} {:oid "2.5.29.37" :critical true - :value [ssl-server-cert ssl-client-cert]} + :value [ca/ssl-server-cert ca/ssl-client-cert]} {:oid "2.5.29.15" :critical true :value #{:digital-signature :key-encipherment}} @@ -1447,7 +1509,7 @@ "subject"] :ip ["192.168.69.90" "192.168.69.91"]}}] - (is (= (create-subject-alt-names-ext "subject" dns-alt-names) exts-expected)))) + (is (= (ca/create-subject-alt-names-ext "subject" dns-alt-names) exts-expected)))) (testing "A non-puppet OID read from a CSR attributes file is rejected" (let [config (assoc (testutils/master-settings confdir) @@ -1456,23 +1518,23 @@ (is (thrown+? [:kind :disallowed-extension :msg "Found extensions that are not permitted: 1.2.3.4"] - (create-master-extensions subject subject-pub cert config))))) + (ca/create-master-extensions subject subject-pub cert config))))) (testing "invalid DNS alt names are rejected" (let [dns-alt-names "*.wildcard"] (is (thrown+? [:kind :invalid-alt-name :msg "Cert subjectAltName contains a wildcard, which is not allowed: *.wildcard"] - (create-master-extensions subject subject-pub cert - (assoc (testutils/master-settings confdir) - :dns-alt-names dns-alt-names)))))) + (ca/create-master-extensions subject subject-pub cert + (assoc (testutils/master-settings confdir) + :dns-alt-names dns-alt-names)))))) (testing "basic extensions are created for a CA" - (let [exts (create-ca-extensions subject-pub - subject-pub) + (let [exts (ca/create-ca-extensions subject-pub + subject-pub) exts-expected [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.35" :critical false :value {:issuer-dn nil @@ -1497,10 +1559,10 @@ (utils/puppet-node-uid "UUUU-IIIII-DDD" false)] csr (utils/generate-certificate-request subject-keys subject-dn csr-exts) - exts (create-agent-extensions csr cert) + exts (ca/create-agent-extensions csr cert) exts-expected [{:oid "2.16.840.1.113730.1.13" :critical false - :value netscape-comment-value} + :value ca/netscape-comment-value} {:oid "2.5.29.35" :critical false :value {:issuer-dn nil @@ -1515,7 +1577,7 @@ :value {:is-ca false}} {:oid "2.5.29.37" :critical true - :value [ssl-server-cert ssl-client-cert]} + :value [ca/ssl-server-cert ca/ssl-client-cert]} {:oid "2.5.29.15" :critical true :value #{:digital-signature :key-encipherment}} @@ -1539,7 +1601,7 @@ (deftest netscape-comment-value-test (testing "Netscape comment constant has expected value" - (is (= "Puppet Server Internal Certificate" netscape-comment-value)))) + (is (= "Puppet Server Internal Certificate" ca/netscape-comment-value)))) (deftest ensure-alt-names-allowed-test (let [subject-keys (utils/generate-key-pair 512) @@ -1555,7 +1617,7 @@ (is (thrown+-with-msg? [:kind :disallowed-extension] #".*new-cert.*subject alternative names.*bad-name.*" - (ensure-subject-alt-names-allowed! csr false)))) + (ca/ensure-subject-alt-names-allowed! csr false)))) (let [alt-name-ext {:oid utils/subject-alt-name-oid :value {:dns-name ["bad-name" subject]} :critical false} @@ -1563,13 +1625,13 @@ (is (thrown+-with-msg? [:kind :disallowed-extension] #".*new-cert.*subject alternative names.*bad-name.*" - (ensure-subject-alt-names-allowed! csr false))))) + (ca/ensure-subject-alt-names-allowed! csr false))))) (testing "allows a single alt name matching the subject" (let [alt-name-ext {:oid utils/subject-alt-name-oid :value {:dns-name [subject]} :critical false} csr (utils/generate-certificate-request subject-keys subject-dn [alt-name-ext])] - (is (nil? (ensure-subject-alt-names-allowed! csr false))) + (is (nil? (ca/ensure-subject-alt-names-allowed! csr false))) (is (logutils/logged? #"Allowing subject alt name" :debug)))))) (testing "when allow-subject-alt-names is true" (testing "allows all alt names" @@ -1577,19 +1639,18 @@ :value {:dns-name [subject "another-name"]} :critical false} csr (utils/generate-certificate-request subject-keys subject-dn [alt-name-ext])] - (is (nil? (ensure-subject-alt-names-allowed! csr true)))))))) + (is (nil? (ca/ensure-subject-alt-names-allowed! csr true)))))))) (deftest ensure-no-authorization-extensions!-test (testing "when checking a csr for authorization extensions" (let [subject-keys (utils/generate-key-pair 512) - subject-pub (utils/get-public-key subject-keys) subject "borges" subject-dn (utils/cn subject) - pp-auth-ext {:oid (:pp_authorization puppet-short-names) + pp-auth-ext {:oid (:pp_authorization ca/puppet-short-names) :value "true" :critical false} - pp-auth-role {:oid (:pp_auth_role puppet-short-names) + pp-auth-role {:oid (:pp_auth_role ca/puppet-short-names) :value "com" :critical false} auth-csr (utils/generate-certificate-request subject-keys subject-dn [pp-auth-ext]) @@ -1599,134 +1660,153 @@ (is (thrown+-with-msg? [:kind :disallowed-extension] #".*borges.*contains an authorization extension.*" - (ensure-no-authorization-extensions! auth-csr false)))) + (ca/ensure-no-authorization-extensions! auth-csr false)))) (testing "pp_auth_role is caught" (is (thrown+-with-msg? [:kind :disallowed-extension] #".*borges.*contains an authorization extension..*" - (ensure-no-authorization-extensions! auth-role-csr false))))))) + (ca/ensure-no-authorization-extensions! auth-role-csr false))))))) (deftest validate-subject!-test (testing "an exception is thrown when the hostnames don't match" (is (thrown+? [:kind :hostname-mismatch :msg "Instance name \"test-agent\" does not match requested key \"not-test-agent\""] - (validate-subject! + (ca/validate-subject! "not-test-agent" "test-agent")))) (testing "an exception is thrown if the subject name contains a capital letter" (is (thrown+? [:kind :invalid-subject-name :msg "Certificate names must be lower case."] - (validate-subject! "Host-With-Capital-Letters" - "Host-With-Capital-Letters")))) + (ca/validate-subject! "Host-With-Capital-Letters" + "Host-With-Capital-Letters")))) (testing "an exception is thrown when the hostnames ends in hyphen" (is (thrown+? [:kind :invalid-subject-name :msg "Subject hostname format is invalid"] - (validate-subject! + (ca/validate-subject! "rootca-.example.org" "rootca-.example.org")))) (testing "an exception is thrown when the hostnames starts with hyphen" (is (thrown+? [:kind :invalid-subject-name :msg "Subject hostname format is invalid"] - (validate-subject! + (ca/validate-subject! "-rootca.example.org" "-rootca.example.org")))) (testing "an exception is thrown when the hostnames contains a space" (is (thrown+? [:kind :invalid-subject-name :msg "Subject hostname format is invalid"] - (validate-subject! + (ca/validate-subject! "root ca.example.org" "root ca.example.org")))) (testing "an exception is thrown when the hostnames contain an ampersand" (is (thrown+? [:kind :invalid-subject-name :msg "Subject hostname format is invalid"] - (validate-subject! + (ca/validate-subject! "root&ca.example.org" "root&ca.example.org")))) (testing "an exception is thrown when the hostname is empty" (is (thrown+? [:kind :invalid-subject-name :msg "Subject hostname format is invalid"] - (validate-subject! + (ca/validate-subject! "" "")))) (testing "an exception is thrown when the hostnames contain multiple dots in a row" (is (thrown+? [:kind :invalid-subject-name :msg "Subject hostname format is invalid"] - (validate-subject! + (ca/validate-subject! "rootca..example.org" "rootca..example.org")))) - (testing "an exception is thrown when the hostnames end in dot" - (is (thrown+? - [:kind :invalid-subject-name - :msg "Subject hostname format is invalid"] - (validate-subject! - "rootca." "rootca.")))) + (testing "subjects that end end in dot are valid" + (is (nil? + (ca/validate-subject! + "rootca." "rootca.")))) + + (testing "subjects that end in an underscore are valid" + (is (nil? + (ca/validate-subject! + "rootca_" "rootca_")))) + + (testing "subjects that start in an underscore are valid" + (is (nil? + (ca/validate-subject! + "_x-puppet._tcp.example.com" "_x-puppet._tcp.example.com")))) + + (testing "single letter segments are valid" + (is (nil? + (ca/validate-subject! + "a.example.com" "a.example.com"))) + (is (nil? + (ca/validate-subject! + "_.example.com" "_.example.com"))) + (is (nil? + (ca/validate-subject! + "foo.a.example.com" "foo.a.example.com")))) (testing "Single word hostnames are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "rootca" "rootca")))) (testing "Domain names are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "puppet.com" "puppet.com")))) (testing "Subdomains are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "ca.puppet.com" "ca.puppet.com")))) (testing "Hostnames containing underscores are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "root_ca" "root_ca")))) (testing "Hostnames containing dashes are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "root-ca" "root-ca")))) (testing "Hostnames containing numbers are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "root123" "root123")))) (testing "Domains containing numbers are allowed" (is (nil? - (validate-subject! + (ca/validate-subject! "root123.com" "root123.com"))))) (deftest validate-subject-alt-names!-test (testing "Both DNS and IP alt names are allowed" (is (nil? - (validate-subject-alt-names! {:oid "2.5.29.17" - :critical false - :value {:ip ["12.34.5.6"] :dns-name ["ahostname"]}})))) + (ca/validate-subject-alt-names! {:oid "2.5.29.17" + :critical false + :value {:ip ["12.34.5.6"] :dns-name ["ahostname"]}})))) (testing "Non-DNS and IP names are not allowed" (is (thrown+? [:kind :invalid-alt-name :msg "Only DNS and IP names are allowed in the Subject Alternative Names extension"] - (validate-subject-alt-names! {:oid "2.5.29.17" - :critical false - :value {:uri ["12.34.5.6"]}})))) + (ca/validate-subject-alt-names! {:oid "2.5.29.17" + :critical false + :value {:uri ["12.34.5.6"]}})))) (testing "No DNS wildcards are allowed" (is (thrown+? [:kind :invalid-alt-name :msg "Cert subjectAltName contains a wildcard, which is not allowed: foo*bar"] - (validate-subject-alt-names! {:oid "2.5.29.17" - :critical false - :value {:dns-name ["ahostname" "foo*bar"]}}))))) + (ca/validate-subject-alt-names! {:oid "2.5.29.17" + :critical false + :value {:dns-name ["ahostname" "foo*bar"]}}))))) (deftest default-master-dns-alt-names (testing "Master certificate has default DNS alt names if none are specified" @@ -1747,7 +1827,7 @@ cert (utils/sign-certificate cn ca-priv-key 666 not-before not-after cn ca-pub-key (utils/create-ca-extensions ca-pub-key ca-pub-key)) - alt-names (-> (create-master-extensions "master" pubkey cert settings) + alt-names (-> (ca/create-master-extensions "master" pubkey cert settings) (utils/get-extension-value utils/subject-alt-name-oid) (:dns-name))] (is (= #{"puppet" "master"} (set alt-names)))))) @@ -1759,8 +1839,8 @@ o all-perms] (let [tmp-file (fs/temp-name "ca-file-perms-test") perms (str u g o)] - (create-file-with-perms tmp-file perms) - (is (= perms (get-file-perms tmp-file))) + (ca/create-file-with-perms tmp-file perms) + (is (= perms (ks-file/get-perms tmp-file))) (fs/delete tmp-file)))) (testing "Changing the perms of an already created file" @@ -1772,9 +1852,9 @@ (when-not (empty? perms) (let [tmp-file (fs/temp-name "ca-file-perms-test") [init-perm change-perm] (take 2 perms)] - (create-file-with-perms tmp-file init-perm) - (set-file-perms tmp-file change-perm) - (is (= change-perm (get-file-perms tmp-file))) + (ca/create-file-with-perms tmp-file init-perm) + (ks-file/set-perms tmp-file change-perm) + (is (= change-perm (ks-file/get-perms tmp-file))) (fs/delete tmp-file) (recur (nthnext perms 2)))))))) @@ -1782,7 +1862,7 @@ (testing "when parsing a csr_attributes file using short names" (testing "and the file exists" (testing "and it has non-whitelisted OIDs we properly translate the short names." - (let [extensions (create-csr-attrs-exts (csr-attributes-file "csr_attributes_with_auth.yaml")) + (let [extensions (ca/create-csr-attrs-exts (csr-attributes-file "csr_attributes_with_auth.yaml")) expected (concat attribute-file-extensions [{:oid "1.3.6.1.4.1.34380.1.3.1" ;; :pp_authorization :critical false @@ -1792,41 +1872,546 @@ :value "com"}])] (is (= (set extensions) (set expected))))) (testing "and it has whitelisted OIDs we properly translate the short names." - (let [extensions (create-csr-attrs-exts (csr-attributes-file "csr_attributes.yaml"))] + (let [extensions (ca/create-csr-attrs-exts (csr-attributes-file "csr_attributes.yaml"))] (is (= (set extensions) (set attribute-file-extensions)))))) (testing "and the file doesn't exist" (testing "the result is nil" - (is (nil? (create-csr-attrs-exts "does/not/exist.yaml"))))))) + (is (nil? (ca/create-csr-attrs-exts "does/not/exist.yaml"))))))) (deftest ca-expiration-dates-test (testing "returns a map of names to dates" (let [settings (testutils/ca-sandbox! bundle-cadir) - expiration-map (ca-expiration-dates (:cacert settings))] + expiration-map (ca/ca-expiration-dates (:cacert settings))] (is (= "2036-09-06T05:58:33UTC" (get expiration-map "rootca.example.org"))) (is (= "2036-09-06T06:09:14UTC" (get expiration-map "intermediateca.example.org")))))) (deftest crl-expiration-dates-test (testing "returns a map of names to dates" (let [settings (testutils/ca-sandbox! bundle-cadir) - expiration-map (crl-expiration-dates (:cacrl settings))] + expiration-map (ca/crl-expiration-dates (:cacrl settings))] (is (= "2016-10-11T06:42:52UTC" (get expiration-map "rootca.example.org"))) (is (= "2016-10-11T06:40:47UTC" (get expiration-map "intermediateca.example.org")))))) (deftest get-cert-or-csr-statuses-test - (let [crl (-> (get-certificate-revocation-list cacrl) + (let [lock (new ReentrantReadWriteLock) + descriptor "test-crl" + timeout 1 + crl (-> (ca/get-certificate-revocation-list cacrl lock descriptor timeout) StringReader. utils/pem->crl)] (testing "returns a collection of 'requested' statuses when queried for CSR" - (let [request-statuses (get-cert-or-csr-statuses csrdir crl false) + (let [request-statuses (ca/get-cert-or-csr-statuses csrdir crl false) result-states (map :state request-statuses)] (is (every? #(= "requested" %) result-states)))) (testing "returns a collection of 'signed' or 'revoked' statuses when queried for cert" - (let [cert-statuses (get-cert-or-csr-statuses signeddir crl true) + (let [cert-statuses (ca/get-cert-or-csr-statuses signeddir crl true) result-states (map :state cert-statuses)] (is (every? #(or (= "signed" %) (= "revoked" %)) result-states)))) (testing "errors when given wrong directory path for querying CSR" (is (thrown? java.lang.ClassCastException - (get-cert-or-csr-statuses signeddir crl false)))))) + (ca/get-cert-or-csr-statuses signeddir crl false)))))) + +(defn create-ca-cert + [name serial] + (let [keypair (utils/generate-key-pair) + public-key (utils/get-public-key keypair) + private-key (utils/get-private-key keypair) + x500-name (utils/cn name) + validity (ca/cert-validity-dates 3600) + ca-exts (ca/create-ca-extensions public-key public-key)] + {:public-key public-key + :private-key private-key + :x500-name x500-name + :certname name + :cert (utils/sign-certificate + x500-name + private-key + serial + (:not-before validity) + (:not-after validity) + x500-name + public-key + ca-exts)})) + +(deftest cert-authority-id-match-ca-subject-id?-test + (let [ca-cert-1 (create-ca-cert "ca1" 1) + ca-cert-2 (create-ca-cert "ca2" 2) + ca-cert-3 (create-ca-cert "ca3" 3) + cert-1 (simple/gen-cert "foo" ca-cert-1 4 {:extensions [(utils/authority-key-identifier (:cert ca-cert-1))]}) + cert-2 (simple/gen-cert "foo" ca-cert-2 5 {:extensions [(utils/authority-key-identifier (:cert ca-cert-2))]}) + cert-3 (simple/gen-cert "foo" ca-cert-3 5 {:extensions [(utils/authority-key-identifier (:cert ca-cert-3))]})] + (testing "Certificates that match CA report as matching" + (is (true? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-1) (:cert ca-cert-1)))) + (is (true? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-2) (:cert ca-cert-2)))) + (is (true? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-3) (:cert ca-cert-3))))) + (testing "Certificates that don't match CA report as not matching" + (is (false? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-1) (:cert ca-cert-2)))) + (is (false? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-1) (:cert ca-cert-3)))) + (is (false? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-2) (:cert ca-cert-1)))) + (is (false? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-2) (:cert ca-cert-3)))) + (is (false? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-3) (:cert ca-cert-1)))) + (is (false? (ca/cert-authority-id-match-ca-subject-id? (:cert cert-3) (:cert ca-cert-2))))))) + +(deftest duration-string-conversion-test + (testing "a valid duration string coverts to expected seconds" + (let [duration-str-1 "1y 1d 1h 1m 1s" + duration-str-2 "800y 0d 0h 0m 0s" + duration-str-3 "22d1s" + duration-str-4 "0s"] + (is (= 31626061 (ca/duration-str->sec duration-str-1))) + (is (= 25228800000 (ca/duration-str->sec duration-str-2))) + (is (= 1900801 (ca/duration-str->sec duration-str-3))) + (is (= 0 (ca/duration-str->sec duration-str-4))))) + (testing "an invalid duration string returns nil" + (let [duration-str-1 "not a duration string 283q 3z 3x 03o" + duration-str-2 "not a duration string 20d 20s" + duration-str-3 "1 y 1__d 30 m s22 m39 thirtym" + duration-str-4 "0y 0d 0h 0m 0s 0x 0y 0z" + duration-str-5 33 + duration-str-6 nil] + (is (= nil (ca/duration-str->sec duration-str-1))) + (is (= nil (ca/duration-str->sec duration-str-2))) + (is (= nil (ca/duration-str->sec duration-str-3))) + (is (= nil (ca/duration-str->sec duration-str-4))) + (is (= nil (ca/duration-str->sec duration-str-5))) + (is (= nil (ca/duration-str->sec duration-str-6)))))) +(deftest renew-certificate!-test + (testing "creates a new signed cert" + (let [settings (testutils/ca-sandbox! cadir) + ;; auto-renewal-cert-ttl is expected to be an int + ;; unit tests skip some of the conversion flow so + ;; transform the duration here + converted-auto-renewal-cert-ttl (ca/duration-str->sec (:auto-renewal-cert-ttl settings)) + updated-settings (assoc settings :auto-renewal-cert-ttl converted-auto-renewal-cert-ttl) + ca-cert (create-ca-cert "ca1" 1) + keypair (utils/generate-key-pair) + subject (utils/cn "foo") + csr (utils/generate-certificate-request keypair subject) + validity (ca/cert-validity-dates 3600) + signed-cert (utils/sign-certificate + (utils/get-subject-from-x509-certificate (:cert ca-cert)) + (:private-key ca-cert) + (ca/next-serial-number! settings) + (:not-before validity) + (:not-after validity) + subject + (utils/get-public-key csr) + (ca/create-agent-extensions csr (:cert ca-cert))) + expected-cert-path (ca/path-to-cert (:signeddir settings) "foo")] + (testing "simulate the cert being written" + (ca/write-cert signed-cert expected-cert-path) + (is (fs/exists? expected-cert-path))) + (Thread/sleep 1000) ;; ensure there is some time elapsed between the two + (let [renewed-cert (ca/renew-certificate! signed-cert updated-settings (constantly nil))] + (is (some? renewed-cert)) + (testing "serial number has increased" + (is (< (.getSerialNumber signed-cert) (.getSerialNumber renewed-cert))) + (is (= 6 (.getSerialNumber renewed-cert)))) + (testing "not before time stamps have changed" + (is (= -1 (.compareTo (.getNotBefore signed-cert) (.getNotBefore renewed-cert))))) + (testing "new not-after is later than before" + (is (= -1 (.compareTo (.getNotAfter signed-cert) (.getNotAfter renewed-cert))))) + (testing "new not-after should be 89 days (and some faction) away" + (let [diff (- (.getTime (.getNotAfter renewed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 89 days)))) + (testing "certificate should have been replaced" + (is (fs/exists? expected-cert-path)) + (testing "updated cert on disk matches renewed cert" + (let [updated-cert (utils/pem->cert expected-cert-path)] + (is (= 6 (.getSerialNumber updated-cert))) + (is (zero? (.compareTo (.getNotBefore updated-cert) (.getNotBefore renewed-cert)))) + (is (zero? (.compareTo (.getNotAfter updated-cert) (.getNotAfter renewed-cert))))))) + (testing "extensions are preserved" + (let [extensions-before (utils/get-extensions signed-cert) + extensions-after (utils/get-extensions signed-cert)] + ;; ordering may be different so use an unordered comparison + (is (= (set extensions-before) + (set extensions-after))))) + (testing "the new entry is written to the inventory file" + (let [entries (string/split (slurp (:cert-inventory settings)) #"\n") + last-entry-fields (string/split (last entries) #" ")] + ;; since the content of the inventory is well established (because of the sandbox), we can + ;; just assert that the last entry is there, and makes sense + ;; there are four fields, serial number, not before, not after, and subject + ;; for ease of testing, just test the first and last + (is (= "0x0006" (first last-entry-fields))) + (is (= "/CN=foo" (last last-entry-fields))))) + (testing "the new entry is not in the infra-serial file" + (is (= "" (slurp (:infra-node-serials-path settings)))))))) + (testing "infra inventory correctly writes files" + (let [settings (testutils/ca-sandbox! cadir) + ;; auto-renewal-cert-ttl is expected to be an int + ;; unit tests skip some of the conversion flow so + ;; transform the duration here + converted-auto-renewal-cert-ttl (ca/duration-str->sec (:auto-renewal-cert-ttl settings)) + updated-settings (assoc settings :auto-renewal-cert-ttl converted-auto-renewal-cert-ttl) + ;; simulate the node being in the infra inventory file + _ (spit (:infra-nodes-path settings) "bar\n") + ca-cert (create-ca-cert "ca1" 1) + keypair (utils/generate-key-pair) + subject (utils/cn "bar") + csr (utils/generate-certificate-request keypair subject) + validity (ca/cert-validity-dates 3600) + signed-cert (utils/sign-certificate + (utils/get-subject-from-x509-certificate (:cert ca-cert)) + (:private-key ca-cert) + (ca/next-serial-number! settings) + (:not-before validity) + (:not-after validity) + subject + (utils/get-public-key csr) + (ca/create-agent-extensions csr (:cert ca-cert))) + expected-cert-path (ca/path-to-cert (:signeddir settings) "bar")] + (testing "simulate the cert being written" + (ca/write-cert signed-cert expected-cert-path) + (is (fs/exists? expected-cert-path))) + (Thread/sleep 1000) ;; ensure there is some time elapsed between the two + (let [renewed-cert (ca/renew-certificate! signed-cert updated-settings (constantly nil))] + (is (some? renewed-cert)) + (testing "serial number has increased" + (is (< (.getSerialNumber signed-cert) (.getSerialNumber renewed-cert))) + (is (= 6 (.getSerialNumber renewed-cert)))) + (testing "not before time stamps have changed" + (is (= -1 (.compareTo (.getNotBefore signed-cert) (.getNotBefore renewed-cert))))) + (testing "new not-after is later than before" + (is (= -1 (.compareTo (.getNotAfter signed-cert) (.getNotAfter renewed-cert))))) + (testing "new not-after should be 89 days (and some faction) away" + (let [diff (- (.getTime (.getNotAfter renewed-cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + (is (= 89 days)))) + (testing "certificate should have been replaced" + (is (fs/exists? expected-cert-path)) + (testing "updated cert on disk matches renewed cert" + (let [updated-cert (utils/pem->cert expected-cert-path)] + (is (= 6 (.getSerialNumber updated-cert))) + (is (zero? (.compareTo (.getNotBefore updated-cert) (.getNotBefore renewed-cert)))) + (is (zero? (.compareTo (.getNotAfter updated-cert) (.getNotAfter renewed-cert))))))) + (testing "extensions are preserved" + (let [extensions-before (utils/get-extensions signed-cert) + extensions-after (utils/get-extensions signed-cert)] + ;; ordering may be different so use an unordered comparison + (is (= (set extensions-before) + (set extensions-after))))) + (testing "the new entry is written to the inventory file" + (let [entries (string/split (slurp (:cert-inventory settings)) #"\n") + last-entry-fields (string/split (last entries) #" ")] + ;; since the content of the inventory is well established (because of the sandbox), we can + ;; just assert that the last entry is there, and makes sense + ;; there are four fields, serial number, not before, not after, and subject + ;; for ease of testing, just test the first and last + (is (= "0x0006" (first last-entry-fields))) + (is (= "/CN=bar" (last last-entry-fields))))) + (testing "the new entry is in the infra-serial file" + (is (= "6\n" (slurp (:infra-node-serials-path settings))))))))) +(deftest supports-auto-renewal?-test + (let [keypair (utils/generate-key-pair) + subject (utils/cn "foo")] + (testing "should not support auto-renewal" + (is (false? (ca/supports-auto-renewal? (utils/generate-certificate-request keypair subject [] [])))) + (is (false? (ca/supports-auto-renewal? (utils/generate-certificate-request keypair subject [] [{:oid ca/pp_auth_auto_renew-attribute :value false}])))) + (is (false? (ca/supports-auto-renewal? (utils/generate-certificate-request keypair subject [] [{:oid ca/pp_auth_auto_renew-attribute :value "false"}]))))) + (testing "should support auto-renewal" + (is (true? (ca/supports-auto-renewal? (utils/generate-certificate-request keypair subject [] [{:oid ca/pp_auth_auto_renew-attribute :value true}])))) + (is (true? (ca/supports-auto-renewal? (utils/generate-certificate-request keypair subject [] [{:oid ca/pp_auth_auto_renew-attribute :value "true"}]))))))) + +(deftest get-csr-attributes-test + (testing "extract attribute from CSR" + (let [keypair (utils/generate-key-pair) + subject (utils/cn "foo") + csr (utils/generate-certificate-request keypair subject [] [{:oid ca/pp_auth_auto_renew-attribute :value true}])] + (is (= [{:oid ca/pp_auth_auto_renew-attribute, :values ["true"]}] (ca/get-csr-attributes csr)))))) + +(deftest crl-expires-in-n-days?-test + (let [settings (testutils/ca-sandbox! cadir)] + ;; by default the crl expires in 5 years + (testing "CRL with long expiration don't expire soon" + (is (false? (ca/crl-expires-in-n-days? (:cacrl settings) settings 5)))) + (testing "CRL with long expiration expires in ~6 years" + (is (ca/crl-expires-in-n-days? (:cacrl settings) settings (* 365 6)))) + (testing "created crl expires in 5 days" + (let [[crl & rest-of-full-chain] (utils/pem->crls (:cacrl settings)) + public-key (utils/pem->public-key (:capub settings)) + private-key (utils/pem->private-key (:cakey settings)) + new-crl (utils/generate-crl + (.getSubjectX500Principal (utils/pem->ca-cert (:cacert settings) (:cakey settings))) + private-key + public-key + (.getThisUpdate crl) + ;; create a date 5 days from now using java.time + (-> (LocalDateTime/now) + (.plusDays 5) + (.toInstant (ZoneOffset/UTC)) + (Date/from)) + (biginteger 1) + nil) + new-full-chain (cons new-crl (vec rest-of-full-chain)) + temp-path (fs/temp-file "crl" "pem") + path-as-string (.getCanonicalPath temp-path)] + (ca/write-crls new-full-chain (.getCanonicalPath temp-path)) + (doseq [i (range 0 4)] + (is (false? (ca/crl-expires-in-n-days? path-as-string settings i)))) + (is (ca/crl-expires-in-n-days? path-as-string settings 5)) + (is (ca/crl-expires-in-n-days? path-as-string settings 6)))))) + +(deftest overwrite-existing-crl!-test + (let [settings (testutils/ca-sandbox! bundle-cadir) + [crl & rest-of-full-chain] (utils/pem->crls (:cacrl settings)) + crl-serials (set (map #(.getSerialNumber %) (.getRevokedCertificates crl))) + all-serials (set/union crl-serials (set (map biginteger (range 1000 2000)))) + ca-cert (utils/pem->ca-cert (:cacert settings) (:cakey settings)) + before-next-update (.getNextUpdate crl) + crl-number (utils/get-crl-number crl)] + (ca/overwrite-existing-crl! crl rest-of-full-chain (:capub settings) (:cakey settings) ca-cert (vec all-serials) (:cacrl settings)) + (testing "overwritten crl has updated properties" + (let [[updated-crl & _rest-of-full-chain] (utils/pem->crls (:cacrl settings))] + (is (.after (.getNextUpdate updated-crl) before-next-update)) + (is (< crl-number (utils/get-crl-number updated-crl))) + (is (not= (set (map #(.getSerialNumber %) (.getRevokedCertificates updated-crl))) + crl-serials)) + (is (= (set (map #(.getSerialNumber %) (.getRevokedCertificates updated-crl))) + all-serials)))))) +(deftest expired-inventory-serials-test + (let [settings (testutils/ca-sandbox! cadir) + keypair (utils/generate-key-pair) + subject (utils/cn "foo") + csr (utils/generate-certificate-request keypair subject) + serial-number (ca/next-serial-number! settings) + ca-cert (utils/pem->ca-cert (:cacert settings) (:cakey settings)) + private-key (utils/pem->private-key (:cakey settings)) + + signed-cert (utils/sign-certificate + (utils/get-subject-from-x509-certificate ca-cert) + private-key + serial-number + ;; valid from 100 days ago until yesterday + (-> (LocalDateTime/now) + (.minusDays 100) + (.toInstant ZoneOffset/UTC) + (Date/from)) + (-> (LocalDateTime/now) + (.minusDays 1) + (.toInstant ZoneOffset/UTC) + (Date/from)) + subject + (utils/get-public-key csr) + (ca/create-agent-extensions csr ca-cert))] + (ca/write-cert-to-inventory! signed-cert settings) + ;; there are multiple entries in the inventory file that aren't expired by default + (is (= [(biginteger serial-number)] (ca/expired-inventory-serials settings))))) + +(deftest update-and-sign-crl!-test + (let [settings (testutils/ca-sandbox! bundle-cadir) + [crl & rest-of-full-chain] (utils/pem->crls (:cacrl settings)) + crl-serials (set (map #(.getSerialNumber %) (.getRevokedCertificates crl))) + crl-number (utils/get-crl-number crl) + serial-number (ca/next-serial-number! settings) + keypair (utils/generate-key-pair) + subject (utils/cn "foo") + csr (utils/generate-certificate-request keypair subject) + ca-cert (utils/pem->ca-cert (:cacert settings) (:cakey settings)) + private-key (utils/pem->private-key (:cakey settings)) + ;; create an add an expired cert to add to the inventory after we update the crl with + ;; the serial number of the cert + signed-cert (utils/sign-certificate + (utils/get-subject-from-x509-certificate ca-cert) + private-key + serial-number + ;; valid from 100 days ago until yesterday + (-> (LocalDateTime/now) + (.minusDays 100) + (.toInstant ZoneOffset/UTC) + (Date/from)) + (-> (LocalDateTime/now) + (.minusDays 1) + (.toInstant ZoneOffset/UTC) + (Date/from)) + subject + (utils/get-public-key csr) + (ca/create-agent-extensions csr ca-cert)) + extra-serials (set (map biginteger (range 1000 2000))) + all-serials (set/union crl-serials #{(biginteger serial-number)} extra-serials)] + (ca/overwrite-existing-crl! crl rest-of-full-chain (:capub settings) (:cakey settings) ca-cert (vec all-serials) (:cacrl settings)) + (let [[updated-crl & _rest-of-full-chain] (utils/pem->crls (:cacrl settings)) + updated-crl-number (utils/get-crl-number updated-crl)] + (is (= (.getName (.getIssuerX500Principal ^X509CRL updated-crl)) + (.getName (.getIssuerX500Principal ^X509CRL crl)))) + (is (< crl-number updated-crl-number)) + (ca/write-cert-to-inventory! signed-cert settings) + (ca/update-and-sign-crl! (:cacrl settings) settings) + (let [[final-updated-crl & _rest-of-full-chain] (utils/pem->crls (:cacrl settings)) + final-crl-number (utils/get-crl-number final-updated-crl) + final-crl-serials (set (map #(.getSerialNumber %) (.getRevokedCertificates final-updated-crl)))] + (is (< updated-crl-number final-crl-number)) + ;; should not contain the serial that was expired + (is (= (set/union crl-serials extra-serials) final-crl-serials)))))) + +(deftest base-16-str->biginteger-test + (testing "returns expected results" + (doseq [i (range 1 10000)] + (is (= (biginteger i) (ca/base-16-str->biginteger (format "0x%x" i))))))) + +(defn generate-csr + [settings + extensions + attributes] + (let [keypair (utils/generate-key-pair) + subject-name (ks/rand-str :alpha-digits 8) + subject (utils/cn subject-name) + csr (utils/generate-certificate-request keypair subject extensions attributes) + csr-path (ca/path-to-cert-request (:csrdir settings) subject-name)] + (utils/obj->pem! csr csr-path) + {:subject subject + :subject-name subject-name + :csr csr + :csr-path csr-path})) + +(deftest sign-multiple-certificate-signing-requests!-test + (let [inventory-file (str (ks/temp-file)) + settings (-> (testutils/ca-sandbox! bundle-cadir) + (assoc :cert-inventory inventory-file + :allow-auto-renewal true) + (update :auto-renewal-cert-ttl ca/duration-str->sec)) + report-activity (fn [_a _b] nil)] + (testing "single entry" + (testing "happy path" + (let [csr-info (generate-csr settings [] [{:oid ca/pp_auth_auto_renew-attribute :value true}])] + (testing "csr should exist before the operation" + (is (fs/exists? (:csr-path csr-info)))) + (testing "correctly signs" + (is (= {:signed [(:subject-name csr-info)] + :no-csr [] + :signing-errors []} + (ca/sign-multiple-certificate-signing-requests! [(:subject-name csr-info)] settings report-activity)))) + (testing "csr is removed after routine" + (is (not (fs/exists? (:csr-path csr-info))))) + (testing "signed cert" + (let [cert-path (ca/path-to-cert (:signeddir settings) (:subject-name csr-info))] + (testing "should exist" + (is (fs/exists? cert-path))) + (testing "signed cert should be valid" + (let [^X509Certificate cert (utils/pem->cert cert-path) + capub (-> (:cacert settings) + (utils/pem->certs) + (first) + (.getPublicKey))] + ;; this will throw an exception, causing the test to fail if it isn't valid. It returns void + (.checkValidity cert) + (is (nil? (.verify cert capub))) + (testing "not after date should be ~90 days in the future" + (let [diff (- (.getTime (.getNotAfter cert)) (.getTime (Date.))) + days (.convert TimeUnit/DAYS diff TimeUnit/MILLISECONDS)] + ;; 89 days plus some number of hour, minutes seconds + (is (= 89 days)))))) + ;; certificate should be in inventory + (testing "should be in the inventory" + (let [inventory (slurp inventory-file) + entries (string/split inventory #"\n")] + (is (= (count entries) 1)) + (let [row (string/split (first entries) #" ")] + ;; row is constructed of serial-number, start date, end date and name + ;; sandbox has some used serial numbers + (is (= "0x0003" (first row))) + (is (= (str "/" (:subject csr-info)) (nth row 3)))))))))) + (testing "correctly rejects a non-existent csr" + (let [random-csr-name (ks/rand-str :alpha-digits 8)] + (is (= {:signed [] + :no-csr [random-csr-name] + :signing-errors []} + (ca/sign-multiple-certificate-signing-requests! [random-csr-name] settings report-activity))))) + (testing "correctly rejects a cert with Subject alternative names" + (let [alt-name-ext {:oid utils/subject-alt-name-oid + :value {:dns-name ["bad-name"]} + :critical false} + csr-info (generate-csr settings [alt-name-ext] [{:oid ca/pp_auth_auto_renew-attribute :value true}])] + (is (= {:signed [] + :no-csr [] + :signing-errors [(:subject-name csr-info)]} + (ca/sign-multiple-certificate-signing-requests! [(:subject-name csr-info)] settings report-activity))))) + (testing "correctly rejects a cert with authorization extensions when disabled" + (let [csr-info (generate-csr settings [{:oid ca/ppAuthCertExt :value "true" :critical false}] [{:oid ca/pp_auth_auto_renew-attribute :value true}])] + (is (= {:signed [] + :no-csr [] + :signing-errors [(:subject-name csr-info)]} + (ca/sign-multiple-certificate-signing-requests! [(:subject-name csr-info)] settings report-activity))))) + (testing "correctly rejects a cert with unapproved extensions" + (let [csr-info (generate-csr settings [{:oid "1.9.9.9.9.9.0" :value "true" :critical false}] [{:oid ca/pp_auth_auto_renew-attribute :value true}])] + (is (= {:signed [] + :no-csr [] + :signing-errors [(:subject-name csr-info)]} + (ca/sign-multiple-certificate-signing-requests! [(:subject-name csr-info)] settings report-activity)))))) + (testing "multiple entry with both bad and good csrs" + (let [count-range (range 0 100) + _ (println "begin generate good csrs") + good-csrs (doall (pmap (fn [_i] (generate-csr settings [] [{:oid ca/pp_auth_auto_renew-attribute :value true}])) + count-range)) + _ (println "begin generate random csr names") + random-csr-names (doall (pmap (fn [_i] (ks/rand-str :alpha-digits 8)) count-range)) + _ (println "begin generate alt-name csrs") + alt-name-ext {:oid utils/subject-alt-name-oid + :value {:dns-name ["bad-name"]} + :critical false} + bad-names (doall (pmap (fn [_i] (generate-csr settings [alt-name-ext] [{:oid ca/pp_auth_auto_renew-attribute :value true}])) + count-range)) + _ (println "begin generate unauthorized csrs") + unauthorized (doall (pmap (fn [_i] (generate-csr settings [{:oid ca/ppAuthCertExt :value "true" :critical false}] [{:oid ca/pp_auth_auto_renew-attribute :value true}])) + count-range)) + _ (println "begin generate unapproved csrs") + unapproved-extensions (doall (pmap (fn [_i] (generate-csr settings [{:oid "1.9.9.9.9.9.0" :value "true" :critical false}] [{:oid ca/pp_auth_auto_renew-attribute :value true}])) + count-range)) + _ (println "combining all of them") + all-csrs (concat good-csrs bad-names unauthorized unapproved-extensions) + _ (println "add in bad names and shuffle") + all-names (shuffle (concat (map :subject-name all-csrs) random-csr-names)) + result (ca/sign-multiple-certificate-signing-requests! all-names settings report-activity) + signed-set (set (:signed result)) + not-found-set (set (:no-csr result)) + unsigned-set (set (:signing-errors result)) + good-csrs-set (set (map :subject-name good-csrs)) + random-names-set (set random-csr-names) + bad-names-set (set (map :subject-name bad-names)) + unauthorized-set (set (map :subject-name unauthorized)) + unapproved-extensions-set (set (map :subject-name unapproved-extensions))] + (testing "all the signed entries should be present" + (is (= good-csrs-set + signed-set)) + (testing "none of the valid csrs should be in the not-signed set" + (is (empty? (clojure.set/intersection unsigned-set good-csrs-set)))) + (testing "all of the random names should be in the not-found-set" + (is (= random-names-set (clojure.set/intersection not-found-set random-names-set)))) + (testing "all of the alt names should be in the not-signed" + (is (= bad-names-set (clojure.set/intersection unsigned-set bad-names-set)))) + (testing "all of the unauthorized names should be in the not-signed" + (is (= unauthorized-set (clojure.set/intersection unsigned-set unauthorized-set)))) + (testing "all of the unapproved names should be in the not-signed" + (is (= unapproved-extensions-set (clojure.set/intersection unsigned-set unapproved-extensions-set))))))))) + +(def default-permissions + (into-array FileAttribute [(ks-file/perms->attribute "rw-------")])) + +(deftest get-paths-to-all-certificate-requests-test + (testing "finds all files in directory ending with the pem suffix" + (let [^File temp-directory (ks/temp-dir) + path-to-file (.toPath temp-directory) + a-pem-file-names (set (for [i (range 0 100)] + (format "a-%d.pem" i))) + b-pem-file-names (set (for [i (range 0 100)] + (format "b-%d.pem" i))) + a-foo-file-names (set (for [i (range 0 100)] + (format "a-%d.foo" i))) + all-pem-file-names (clojure.set/union a-pem-file-names b-pem-file-names)] + + ;; create a lot of files that match that end with pem + (doall + (for [^String i all-pem-file-names] + (Files/createFile (.resolve ^Path path-to-file i) default-permissions))) + ;; create a lot of files that don't end that start with pem + (doall + (for [^String i a-foo-file-names] + (Files/createFile (.resolve path-to-file i) default-permissions))) + (let [result (ca/get-paths-to-all-certificate-requests (.toString temp-directory)) + file-names (set (common/extract-file-names-from-paths result))] + (is (= (set file-names) all-pem-file-names)))))) \ No newline at end of file diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/puppetserver/ringutils_test.clj puppetserver-8.4.0/test/unit/puppetlabs/puppetserver/ringutils_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/puppetserver/ringutils_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/puppetserver/ringutils_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,6 @@ (ns puppetlabs.puppetserver.ringutils-test - (:require [clojure.test :refer :all] - [puppetlabs.puppetserver.ringutils :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] + [puppetlabs.puppetserver.ringutils :refer [wrap-with-cert-whitelist-check]] [puppetlabs.ssl-utils.core :as ssl-utils] [schema.test :as schema-test])) @@ -23,7 +23,7 @@ (ssl-utils/pem->cert (test-pem-file "revoked-agent.pem"))) (def base-handler - (fn [request] + (fn [_req] {:status 200 :body "hello"})) (defn build-ring-handler diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj puppetserver-8.4.0/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/puppetserver/ruby/http_client_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -6,10 +6,9 @@ (java.util HashMap) (java.io IOException) (java.util.zip GZIPInputStream)) - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.trapperkeeper.testutils.logging :as logutils] - [puppetlabs.trapperkeeper.testutils.webserver :as jetty9] - [puppetlabs.trapperkeeper.testutils.webserver.common :refer [http-get]] + [puppetlabs.trapperkeeper.testutils.webserver :as jetty10] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-puppet-testutils] [ring.middleware.basic-authentication :as auth] [schema.core :as schema] @@ -27,12 +26,12 @@ (str "./dev-resources/puppetlabs/puppetserver/ruby/http_client_test/" filename)) (defn ring-app - [req] + [_req] {:status 200 :body "hi"}) (defn ring-app-alternate - [req] + [_req] {:status 200 :body "bye"}) @@ -71,9 +70,9 @@ (defn get-http-client-settings [options] (let [result (HashMap.)] - (if (contains? options :ssl-protocols) + (when (contains? options :ssl-protocols) (.put result "ssl_protocols" (into-array String (:ssl-protocols options)))) - (if (contains? options :cipher-suites) + (when (contains? options :cipher-suites) (.put result "cipher_suites" (into-array String (:cipher-suites options)))) result)) @@ -168,10 +167,10 @@ (binding [*scripting-container* sc] (test-fn)))) -(clojure.test/use-fixtures :once http-client-scripting-container-fixture) +(use-fixtures :once http-client-scripting-container-fixture) (deftest test-ruby-http-client - (jetty9/with-test-webserver ring-app port + (jetty10/with-test-webserver ring-app port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -181,7 +180,7 @@ (is (= "hi" (.runScriptlet sc (format "$c.post(URI('%s'), 'foo').body" url)))))))))) (deftest http-escaped-urls-test - (jetty9/with-test-webserver ring-app port + (jetty10/with-test-webserver ring-app port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -191,7 +190,7 @@ (is (= (str url "/a%20b%3Fc?foo=bar") (.runScriptlet sc "$response.url.to_s"))))))))) (deftest http-basic-auth - (jetty9/with-test-webserver ring-app-with-auth port + (jetty10/with-test-webserver ring-app-with-auth port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -214,7 +213,7 @@ (is (= "access denied" (.runScriptlet sc "$response.body"))))))))) (deftest http-compressed-requests - (jetty9/with-test-webserver ring-app-decompressing-gzipped-request port + (jetty10/with-test-webserver ring-app-decompressing-gzipped-request port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -245,7 +244,7 @@ (defmacro with-webserver-with-protocols [protocols cipher-suites & body] - `(jetty9/with-test-webserver-and-config ring-app port# + `(jetty10/with-test-webserver-and-config ring-app port# (merge {:ssl-host "localhost" :ssl-port 10080 :ssl-ca-cert ca-pem @@ -305,7 +304,7 @@ (deftest clients-persist (testing "client persists when making HTTP requests" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app port + (jetty10/with-test-webserver ring-app port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port) @@ -314,7 +313,7 @@ (is (= client1 client2)))))))) (testing "all instances of HttpClient have the same underlying client object" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app port + (jetty10/with-test-webserver ring-app port (with-scripting-container sc (with-http-client sc {} (let [client1 (.runScriptlet sc "$c.class.client") @@ -325,7 +324,7 @@ (deftest connections-closed (testing "connection header always set to close on get" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app-connection-closed port + (jetty10/with-test-webserver ring-app-connection-closed port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -333,7 +332,7 @@ (.runScriptlet sc (format "$c.get(URI('%s')).body" url)))))))))) (testing "connection header always set to close on post" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app-connection-closed port + (jetty10/with-test-webserver ring-app-connection-closed port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -341,7 +340,7 @@ (.runScriptlet sc (format "$c.post(URI('%s'), 'foo').body" url)))))))))) (testing "client's terminate function closes the client" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app-connection-closed port + (jetty10/with-test-webserver ring-app-connection-closed port (with-scripting-container sc (with-http-client sc {} (let [url (str "http://localhost:" port)] @@ -359,7 +358,7 @@ (deftest http-and-https (testing "can make http calls after https calls without a new scripting container" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app-alternate port + (jetty10/with-test-webserver ring-app-alternate port (with-webserver-with-protocols nil nil (with-scripting-container sc (with-http-client sc {} @@ -374,7 +373,7 @@ (testing "can make https calls after http calls without a new scripting container" (logutils/with-test-logging - (jetty9/with-test-webserver ring-app-alternate port + (jetty10/with-test-webserver ring-app-alternate port (with-webserver-with-protocols nil nil (with-scripting-container sc (with-http-client sc {} diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/ca/ca_testutils.clj puppetserver-8.4.0/test/unit/puppetlabs/services/ca/ca_testutils.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/ca/ca_testutils.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/ca/ca_testutils.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,10 +1,11 @@ (ns puppetlabs.services.ca.ca-testutils - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [is]] [me.raynes.fs :as fs] [puppetlabs.kitchensink.core :as ks] - [puppetlabs.puppetserver.certificate-authority :as ca] + [puppetlabs.kitchensink.file :as ks-file] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils]) - (:import (java.io ByteArrayInputStream))) + (:import (java.io ByteArrayInputStream) + (java.util.concurrent.locks ReentrantReadWriteLock))) (defn assert-subject [o subject] (is (= subject (-> o .getSubjectX500Principal .getName)))) @@ -54,8 +55,11 @@ :allow-authorization-extensions false :allow-duplicate-certs false :allow-subject-alt-names false + :allow-auto-renewal false + :auto-renewal-cert-ttl "90d" :ca-name "test ca" :ca-ttl 1 + :allow-header-cert-info false :cadir (str cadir) :cacrl (str cadir "/ca_crl.pem") :cacert (str cadir "/ca_crt.pem") @@ -69,10 +73,16 @@ :serial (str cadir "/serial") :ruby-load-path jruby-testutils/ruby-load-path :gem-path jruby-testutils/gem-path - :infra-nodes-path (str cadir "/ca/infra_inventory.txt") + :infra-nodes-path (str cadir "/infra_inventory.txt") :infra-node-serials-path (str cadir "/infra_serials") :infra-crl-path (str cadir "/infra_crl.pem") - :enable-infra-crl false}) + :enable-infra-crl false + :serial-lock (new ReentrantReadWriteLock) + :serial-lock-timeout-seconds 5 + :crl-lock (new ReentrantReadWriteLock) + :crl-lock-timeout-seconds 5 + :inventory-lock (new ReentrantReadWriteLock) + :inventory-lock-timeout-seconds 5}) (defn ca-sandbox! "Copy the `cadir` to a temporary directory and return @@ -82,5 +92,5 @@ (let [tmp-ssldir (ks/temp-dir)] (fs/copy-dir cadir tmp-ssldir) ;; This is to ensure no warnings are logged during tests - (ca/set-file-perms (str tmp-ssldir "/ca/ca_key.pem") "rw-r-----") + (ks-file/set-perms (str tmp-ssldir "/ca/ca_key.pem") "rw-r-----") (ca-settings (str tmp-ssldir "/ca")))) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/ca/certificate_authority_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/ca/certificate_authority_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/ca/certificate_authority_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/ca/certificate_authority_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,14 +1,19 @@ (ns puppetlabs.services.ca.certificate-authority-core-test (:require [cheshire.core :as json] [clojure.java.io :as io] - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [me.raynes.fs :as fs] [puppetlabs.ssl-utils.core :as utils] [puppetlabs.kitchensink.core :as ks] [puppetlabs.puppetserver.certificate-authority :as ca] [puppetlabs.services.ca.ca-testutils :as testutils] - [puppetlabs.services.ca.certificate-authority-core :refer :all] - [puppetlabs.trapperkeeper.testutils.logging :as logutils] + [puppetlabs.services.ca.certificate-authority-core :refer [get-wrapped-handler + web-routes + handle-get-certificate-revocation-list + handle-put-certificate-revocation-list! + handle-put-certificate-request! + handle-delete-certificate-request!]] + [puppetlabs.trapperkeeper.testutils.logging :refer [logged?] :as logutils] [ring.mock.request :as mock] [schema.test :as schema-test] [puppetlabs.comidi :as comidi])) @@ -38,7 +43,7 @@ (defn build-ring-handler [settings puppet-version] (get-wrapped-handler - (-> (web-routes settings) + (-> (web-routes settings (constantly nil)) (comidi/routes->handler)) settings "" @@ -145,8 +150,12 @@ (testing "implementation of the CRL endpoint with no 'If-Modified-Since' header" (let [request (mock/request :get "/v1/certificate_revocation_list/mynode") + ca-settings (testutils/ca-settings cadir) response (handle-get-certificate-revocation-list - request {:cacrl (test-pem-file "crl.pem") :infra-crl-path (test-pem-file "crl.pem") :enable-infra-crl false})] + request (assoc ca-settings + :cacrl (test-pem-file "crl.pem") + :infra-crl-path (test-pem-file "crl.pem") + :enable-infra-crl false))] (is (map? response)) (is (= 200 (:status response))) (is (= "text/plain" (get-in response [:headers "Content-Type"]))) @@ -157,24 +166,59 @@ :request-method :get :headers {"If-Modified-Since" "Wed, 21 Oct 2015 07:28:00"}} response (ring-app request)] - (is (= 200 (:status response)) - (is (string? (:body response)))))) + (is (= 200 (:status response))) + (is (string? (:body response))))) (testing "with older 'If-Modified-Since' header" (let [ring-app (build-ring-handler (testutils/ca-settings cadir) "42.42.42") request {:uri "/v1/certificate_revocation_list/mynode" :request-method :get :headers {"If-Modified-Since" "Wed, 21 Oct 2015 07:28:00 GMT"}} response (ring-app request)] - (is (= 200 (:status response)) - (is (string? (:body response)))))) + (is (= 200 (:status response))) + (is (string? (:body response))))) (testing "with newer 'If-Modified-Since' header" (let [ring-app (build-ring-handler (testutils/ca-settings cadir) "42.42.42") request {:uri "/v1/certificate_revocation_list/mynode" :request-method :get :headers {"If-Modified-Since" "Wed, 21 Oct 3015 07:28:00 GMT"}} response (ring-app request)] - (is (= 304 (:status response)) - (is (nil? (:body response))))))) + (is (= 304 (:status response))) + (is (nil? (:body response)))))) + +(deftest certificate-endpoint-test + (testing "implementation of the CRL endpoint with no 'If-Modified-Since' header" + (let [ring-app (build-ring-handler (testutils/ca-settings cadir) "42.42.42") + request {:uri "/v1/certificate/localhost" + :request-method :get + :headers {}} + response (ring-app request)] + (is (= 200 (:status response))) + (is (string? (:body response))) + (is (= "text/plain" (get-in response [:headers "Content-Type"]))))) + (testing "with a malformed http-date 'If-Modified-Since' header" + (let [ring-app (build-ring-handler (testutils/ca-settings cadir) "42.42.42") + request {:uri "/v1/certificate/localhost" + :request-method :get + :headers {"If-Modified-Since" "Wed, 21 Oct 2015 07:28:00"}} + response (ring-app request)] + (is (= 200 (:status response))) + (is (string? (:body response))))) + (testing "with older 'If-Modified-Since' header" + (let [ring-app (build-ring-handler (testutils/ca-settings cadir) "42.42.42") + request {:uri "/v1/certificate/localhost" + :request-method :get + :headers {"If-Modified-Since" "Wed, 21 Oct 2015 07:28:00 GMT"}} + response (ring-app request)] + (is (= 200 (:status response))) + (is (string? (:body response))))) + (testing "with newer 'If-Modified-Since' header" + (let [ring-app (build-ring-handler (testutils/ca-settings cadir) "42.42.42") + request {:uri "/v1/certificate/localhost" + :request-method :get + :headers {"If-Modified-Since" "Wed, 21 Oct 3015 07:28:00 GMT"}} + response (ring-app request)] + (is (= 304 (:status response))) + (is (nil? (:body response)))))) (deftest handle-put-certificate-revocation-list!-test (let [{:keys [cacrl cacert] :as settings} (testutils/ca-sandbox! cadir) @@ -267,21 +311,22 @@ (deftest handle-delete-certificate-request!-test (let [settings (assoc (testutils/ca-sandbox! cadir) :allow-duplicate-certs true - :autosign false)] + :autosign false) + request {:authorization {:name "authname"} :remote-addr "1.1.1.1"}] (testing "successful csr deletion" (logutils/with-test-logging (let [subject "happy-agent" csr-stream (gen-csr-input-stream! subject) - expected-path (ca/path-to-cert-request (:csrdir settings) subject)] + expected-path (ca/path-to-cert-request (:csrdir settings) subject) + request (assoc request :route-params {:subject subject} :body csr-stream)] (try - (handle-put-certificate-request! subject csr-stream settings) + (handle-put-certificate-request! settings (constantly nil) request) (is (true? (fs/exists? expected-path))) (let [response (handle-delete-certificate-request! subject settings) msg-matcher (re-pattern (str "Deleted .* for " subject ".*"))] (is (false? (fs/exists? expected-path))) (is (= 204 (:status response))) (is (re-matches msg-matcher (:body response))) - (is (= "text/plain" (get-in response [:headers "Content-Type"]))) (is (logged? msg-matcher :debug))) (finally (fs/delete expected-path)))))) @@ -301,9 +346,10 @@ (logutils/with-test-logging (let [subject "err-agent" csr-stream (gen-csr-input-stream! subject) - expected-path (ca/path-to-cert-request (:csrdir settings) subject)] + expected-path (ca/path-to-cert-request (:csrdir settings) subject) + request (assoc request :route-params {:subject subject} :body csr-stream)] (try - (handle-put-certificate-request! subject csr-stream settings) + (handle-put-certificate-request! settings (constantly nil) request) (is (true? (fs/exists? expected-path))) (fs/chmod "-w" (fs/parent expected-path)) (let [response (handle-delete-certificate-request! subject settings) @@ -318,7 +364,8 @@ (deftest handle-put-certificate-request!-test (let [settings (assoc (testutils/ca-sandbox! cadir) :allow-duplicate-certs true) - static-csr (ca/path-to-cert-request csrdir "test-agent")] + static-csr (ca/path-to-cert-request csrdir "test-agent") + request {:authorization {:name "ca certname"} :remote-addr "1.1.1.1" :route-params {:subject "test-agent"}}] (logutils/with-test-logging (testing "when autosign results in true" (doseq [value [true @@ -326,13 +373,14 @@ (test-autosign-file "autosign-whitelist.conf")]] (let [settings (assoc settings :autosign value) csr-stream (io/input-stream static-csr) - expected-path (ca/path-to-cert (:signeddir settings) "test-agent")] + expected-path (ca/path-to-cert (:signeddir settings) "test-agent") + request (assoc request :body csr-stream)] (testing "it signs the CSR, writes the certificate to disk, and returns a 200 response with empty plaintext body" (try (is (false? (fs/exists? expected-path))) - (let [response (handle-put-certificate-request! "test-agent" csr-stream settings)] + (let [response (handle-put-certificate-request! settings (constantly nil) request)] (is (true? (fs/exists? expected-path))) (is (= 200 (:status response))) (is (= "text/plain" (get-in response [:headers "Content-Type"]))) @@ -347,14 +395,13 @@ (.getEncoded)) settings (assoc settings :autosign true :cacert ca-cert-file) csr-stream (io/input-stream static-csr) - expected-path (ca/path-to-cert (:signeddir settings) "test-agent")] + expected-path (ca/path-to-cert (:signeddir settings) "test-agent") + request (assoc request :body csr-stream)] (testing "a multi-RDN CA subject is properly set as signed cert's issuer" (try (is (false? (fs/exists? expected-path))) - (let [response (handle-put-certificate-request! "test-agent" - csr-stream - settings) + (let [response (handle-put-certificate-request! settings (constantly nil) request) signed-cert-issuer-bytes (-> (utils/pem->cert expected-path) (.getIssuerX500Principal) (.getEncoded))] @@ -373,13 +420,14 @@ (test-autosign-file "autosign-whitelist.conf")]] (let [settings (assoc settings :autosign value) csr-stream (io/input-stream (test-pem-file "foo-agent-csr.pem")) - expected-path (ca/path-to-cert-request (:csrdir settings) "foo-agent")] + expected-path (ca/path-to-cert-request (:csrdir settings) "foo-agent") + request (assoc request :route-params {:subject "foo-agent"} :body csr-stream)] (testing "it writes the CSR to disk and returns a 200 response with empty plaintext body" (try (is (false? (fs/exists? expected-path))) - (let [response (handle-put-certificate-request! "foo-agent" csr-stream settings)] + (let [response (handle-put-certificate-request! settings (constantly nil) request)] (is (true? (fs/exists? expected-path))) (is (false? (fs/exists? (ca/path-to-cert (:signeddir settings) "foo-agent")))) (is (= 200 (:status response))) @@ -391,10 +439,11 @@ (testing "when $allow-duplicate-certs is false and we receive a new CSR, return a 400 response and error message" (let [settings (assoc settings :allow-duplicate-certs false) - csr-stream (io/input-stream static-csr)] + csr-stream (io/input-stream static-csr) + request (assoc request :route-params {:subject "test-agent"} :body csr-stream)] ;; Put the duplicate in place (fs/copy static-csr (ca/path-to-cert-request (:csrdir settings) "test-agent")) - (let [response (handle-put-certificate-request! "test-agent" csr-stream settings)] + (let [response (handle-put-certificate-request! settings (constantly nil) request)] (is (logged? #"ignoring certificate request" :error)) (is (= 400 (:status response))) (is (true? (.contains (:body response) "ignoring certificate request")))))) @@ -402,7 +451,8 @@ (testing "when the subject CN on a CSR does not match the hostname specified in the URL, the response is a 400" (let [csr-stream (io/input-stream static-csr) - response (handle-put-certificate-request! "NOT-test-agent" csr-stream settings)] + request (assoc request :route-params {:subject "NOT-test-agent"} :body csr-stream) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (re-matches #"Instance name \"test-agent\" does not match requested key \"NOT-test-agent\"" @@ -411,8 +461,8 @@ (testing "when the public key on the CSR is bogus, the response is a 400" (let [csr-with-bad-public-key (test-pem-file "luke.madstop.com-bad-public-key.pem") csr-stream (io/input-stream csr-with-bad-public-key) - response (handle-put-certificate-request! - "luke.madstop.com" csr-stream settings)] + request (assoc request :route-params {:subject "luke.madstop.com"} :body csr-stream) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (= "CSR contains a public key that does not correspond to the signing key" (:body response))))) @@ -420,16 +470,16 @@ (testing "when the CSR has disallowed extensions on it, the response is a 400" (let [csr-with-bad-ext (test-pem-file "meow-bad-extension.pem") csr-stream (io/input-stream csr-with-bad-ext) - response (handle-put-certificate-request! - "meow" csr-stream settings)] + request (assoc request :route-params {:subject "meow"} :body csr-stream) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (= "Found extensions that are not permitted: 1.9.9.9.9.9.9" (:body response)))) (let [csr-with-bad-ext (test-pem-file "woof-bad-extensions.pem") csr-stream (io/input-stream csr-with-bad-ext) - response (handle-put-certificate-request! - "woof" csr-stream settings)] + request (assoc request :route-params {:subject "woof"} :body csr-stream) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (= "Found extensions that are not permitted: 1.9.9.9.9.9.0, 1.9.9.9.9.9.1" (:body response))))) @@ -447,8 +497,8 @@ (doseq [{:keys [subject csr]} bad-csrs] (let [csr-stream (io/input-stream csr) - response (handle-put-certificate-request! - subject csr-stream settings)] + request (assoc request :route-params {:subject subject} :body csr-stream) + response (handle-put-certificate-request! settings nil request)] (is (= 400 (:status response))) (is (= "Subject hostname format is invalid" (:body response))))))) @@ -456,8 +506,8 @@ (testing "no wildcards allowed" (let [csr-with-wildcard (test-pem-file "bad-subject-name-wildcard.pem") csr-stream (io/input-stream csr-with-wildcard) - response (handle-put-certificate-request! - "foo*bar" csr-stream settings)] + request (assoc request :route-params {:subject "foo*bar"} :body csr-stream) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (= "Subject contains a wildcard, which is not allowed: foo*bar" (:body response))))) @@ -465,17 +515,18 @@ (testing "a CSR w/ DNS alt-names and disallowed subject-alt-names gets a specific error response" (let [csr (io/input-stream (test-pem-file "hostwithaltnames.pem")) settings (assoc settings :allow-subject-alt-names false) - response (handle-put-certificate-request! - "hostwithaltnames" csr settings)] + request (assoc request :route-params {:subject "hostwithaltnames"} :body csr) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (re-find #"hostwithaltnames.*disallowed" (:body response))))) (testing "a CSR w/ DNS alt-names and allowed subject-alt-names returns 200" (let [csr (io/input-stream (test-pem-file "hostwithaltnames.pem")) settings (assoc settings :allow-subject-alt-names true) - expected-path (ca/path-to-cert-request (:csrdir settings) "hostwithaltnames")] + expected-path (ca/path-to-cert-request (:csrdir settings) "hostwithaltnames") + request (assoc request :route-params {:subject "hostwithaltnames"} :body csr)] (try - (let [response (handle-put-certificate-request! "hostwithaltnames" csr settings)] + (let [response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 200 (:status response))) (is (= "text/plain" (get-in response [:headers "Content-Type"]))) (is (nil? (:body response)))) @@ -485,17 +536,18 @@ (testing "a CSR w/ DNS and IP alt-names and disallowed subject-alt-names gets a specific error response" (let [csr (io/input-stream (test-pem-file "host-with-ip-and-dns-altnames.pem")) settings (assoc settings :allow-subject-alt-names false) - response (handle-put-certificate-request! - "host-with-ip-and-dns-altnames" csr settings)] + request (assoc request :route-params {:subject "host-with-ip-and-dns-altnames"} :body csr) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) (is (re-find #"host-with-ip-and-dns-altnames" (:body response))))) (testing "a CSR w/ DNS and IP alt-names and allowed subject-alt-names returns 200" (let [csr (io/input-stream (test-pem-file "host-with-ip-and-dns-altnames.pem")) settings (assoc settings :allow-subject-alt-names true) - expected-path (ca/path-to-cert-request (:csrdir settings) "host-with-ip-and-dns-altnames")] + expected-path (ca/path-to-cert-request (:csrdir settings) "host-with-ip-and-dns-altnames") + request (assoc request :route-params {:subject "host-with-ip-and-dns-altnames"} :body csr)] (try - (let [response (handle-put-certificate-request! "host-with-ip-and-dns-altnames" csr settings)] + (let [response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 200 (:status response))) (is (= "text/plain" (get-in response [:headers "Content-Type"]))) (is (nil? (:body response)))) @@ -505,9 +557,10 @@ (testing "a CSR w/ auth extensions and allowed auth extensions returns 200" (let [csr (io/input-stream (test-pem-file "csr-auth-extension.pem")) settings (assoc settings :allow-authorization-extensions true) - expected-path (ca/path-to-cert-request (:csrdir settings) "csr-auth-extension")] + expected-path (ca/path-to-cert-request (:csrdir settings) "csr-auth-extension") + request (assoc request :route-params {:subject "csr-auth-extension"} :body csr)] (try - (let [response (handle-put-certificate-request! "csr-auth-extension" csr settings)] + (let [response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 200 (:status response))) (is (= "text/plain" (get-in response [:headers "Content-Type"]))) (is (nil? (:body response)))) @@ -517,9 +570,54 @@ (testing "a CSR w/ auth extensions and disallowed auth extensions gets a specific error response" (let [csr (io/input-stream (test-pem-file "csr-auth-extension.pem")) settings (assoc settings :allow-authorization-extensions false) - response (handle-put-certificate-request! "csr-auth-extension" csr settings)] + request (assoc request :route-params {:subject "csr-auth-extension"} :body csr) + response (handle-put-certificate-request! settings (constantly nil) request)] (is (= 400 (:status response))) - (is (re-find #"csr-auth-extension.*disallowed" (:body response)))))))) + (is (re-find #"csr-auth-extension.*disallowed" (:body response)))) + + (testing "when authname provided on signing" + (logutils/with-test-logging + (let [csr-stream (io/input-stream static-csr) + signee "authname" + certname "test-agent" + request {:authorization {:name signee} :remote-addr "1.1.1.1" :body csr-stream :route-params {:subject certname}} + msg-matcher (re-pattern (str "Entity " signee " signed 1 certificate: " certname)) + expected-path (ca/path-to-cert (:signeddir settings) certname)] + (try + (let [response (handle-put-certificate-request! settings (constantly nil) request)] + (is (= 200 (:status response))) + (is (logged? msg-matcher :info))) + (finally + (fs/delete expected-path)))))) + + (testing "when rbac-subject provided on signing" + (logutils/with-test-logging + (let [csr-stream (io/input-stream static-csr) + signee "rbac-subject" + certname "test-agent" + request {:rbac-subject {:login signee} :remote-addr "1.1.1.1" :body csr-stream :route-params {:subject certname}} + msg-matcher (re-pattern (str "Entity " signee " signed 1 certificate: " certname)) + expected-path (ca/path-to-cert (:signeddir settings) certname)] + (try + (let [response (handle-put-certificate-request! settings (constantly nil) request)] + (is (= 200 (:status response))) + (is (logged? msg-matcher :info))) + (finally + (fs/delete expected-path)))))) + + (testing "when no signee info provided" + (logutils/with-test-logging + (let [csr-stream (io/input-stream static-csr) + certname "test-agent" + request {:remote-addr "1.1.1.1" :body csr-stream :route-params {:subject certname}} + msg-matcher (re-pattern (str "Entity CA signed 1 certificate: " certname)) + expected-path (ca/path-to-cert (:signeddir settings) certname)] + (try + (let [response (handle-put-certificate-request! settings (constantly nil) request)] + (is (= 200 (:status response))) + (is (logged? msg-matcher :info))) + (finally + (fs/delete expected-path)))))))))) (deftest certificate-status-test (testing "read requests" diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/ca/certificate_authority_disabled_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/ca/certificate_authority_disabled_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/ca/certificate_authority_disabled_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/ca/certificate_authority_disabled_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.ca.certificate-authority-disabled-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [me.raynes.fs :as fs] [puppetlabs.services.ca.certificate-authority-disabled-service :as disabled] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/config/puppet_server_config_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/config/puppet_server_config_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/config/puppet_server_config_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/config/puppet_server_config_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,6 +1,10 @@ (ns puppetlabs.services.config.puppet-server-config-core-test - (:require [clojure.test :refer :all] - [puppetlabs.services.config.puppet-server-config-core :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] + [puppetlabs.services.config.puppet-server-config-core :refer [Config + get-puppet-config* + get-puppet-config-value + init-webserver! + puppet-config-keys]] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.puppetserver.testutils :as testutils] [schema.core :as schema] @@ -80,26 +84,21 @@ :ssl-ca-cert "thelocalcacert" :ssl-crl-path "thehostcrl"}] - (testing (str "no call made to override default webserver settings if " - "full ssl cert configuration already in webserver settings") + (testing "no call made to override default webserver settings if full ssl cert configuration already in webserver settings" (is (nil? (init-webserver-fn webserver-ssl-config)) "Override function unexpectedly called with non-nil args")) - (testing (str "no call made to override default webserver settings if " - "at least one overridable setting already in webserver " - "settings") + (testing "no call made to override default webserver settings if at least one overridable setting already in webserver settings" (doseq [[setting-key setting-value] webserver-ssl-config] (let [map-with-one-overridable-setting {setting-key setting-value}] (is (nil? (init-webserver-fn map-with-one-overridable-setting)) (str "Override function unexpectedly called with non-nil args " "for " map-with-one-overridable-setting))))) - (testing (str "expected settings passed to override function when " - "no overridable ones already exist in webserver settings") + (testing "expected settings passed to override function when no overridable ones already exist in webserver settings" (is (= webserver-ssl-config (init-webserver-fn {})) "Unexpected settings passed into the override function")) - (testing (str "expected settings passed to override function when " - "no overridable ones already exist in webserver settings") + (testing "expected settings passed to override function when no overridable ones already exist in webserver settings" (is (= webserver-ssl-config (init-webserver-fn {:x-non-overridable true})) "Unexpected settings passed into the override function")))) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/config/puppet_server_config_service_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/config/puppet_server_config_service_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/config/puppet_server_config_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/config/puppet_server_config_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,13 +1,13 @@ (ns puppetlabs.services.config.puppet-server-config-service-test - (:require [clojure.test :refer :all] - [puppetlabs.services.protocols.puppet-server-config :refer :all] - [puppetlabs.services.config.puppet-server-config-service :refer :all] + (:require [clojure.test :refer [deftest is testing]] + [puppetlabs.services.protocols.puppet-server-config :refer [get-config get-in-config]] + [puppetlabs.services.config.puppet-server-config-service :refer [puppet-server-config-service]] [puppetlabs.services.config.puppet-server-config-core :as core] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.trapperkeeper.app :as tk-app] [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-testutils] - [puppetlabs.trapperkeeper.testutils.logging :refer [with-test-logging]] - [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] + [puppetlabs.puppetserver.certificate-authority :as ca] + [puppetlabs.trapperkeeper.testutils.logging :refer [with-test-logging logged?]] [clj-semver.core :as semver] [puppetlabs.trapperkeeper.core :as tk] [puppetlabs.trapperkeeper.internal :as tk-internal] @@ -83,9 +83,7 @@ "default value, should not be returned"))))))))) (deftest config-key-conflicts - (testing (str - "Providing config values that should be read from Puppet results " - "in an error that mentions all offending config keys.") + (testing "Providing config values that should be read from Puppet results in an error that mentions all offending config keys." (with-test-logging (ks-testutils/with-no-jvm-shutdown-hooks (let [config (assoc required-config :cacrl "bogus" :cacert "meow") @@ -98,6 +96,64 @@ (tk-internal/throw-app-error-if-exists! app))) (tk-app/stop app)))))) +(deftest certificate-authority-override + (tk-testutils/with-app-with-config + app + service-and-deps + (-> required-config + (assoc :my-config {:foo "bar"})) + (testing (str "certificate-authority settings work") + (with-test-logging + (ks-testutils/with-no-jvm-shutdown-hooks + (let [config (-> (jruby-testutils/jruby-puppet-tk-config + (jruby-testutils/jruby-puppet-config {:max-active-instances 2 + :borrow-timeout + 12})) + (assoc :webserver {:port 8081 + :shutdown-timeout-seconds 1})) + service (tk-app/get-service app :PuppetServerConfigService) + service-config (get-config service) + merged-config (merge service-config {:certificate-authority + {:allow-auto-renewal true + :auto-renewal-cert-ttl "50d" + :ca-ttl "50d"}}) + settings (ca/config->ca-settings merged-config) + app (tk/boot-services-with-config + (service-and-deps-with-mock-jruby config) + config)] + (is (= true (:allow-auto-renewal settings))) + (is (= 4320000 (:auto-renewal-cert-ttl settings))) + (is (= 4320000 (:ca-ttl settings))) + (is (logged? #"Detected ca-ttl setting in CA config which will take precedence over puppet.conf setting")) + (tk-app/stop app))))))) + +(deftest certificate-authority-defaults + (tk-testutils/with-app-with-config + app + service-and-deps + (-> required-config + (assoc :my-config {:foo "bar"})) + (testing (str "certificate-authority settings work") + (with-test-logging + (ks-testutils/with-no-jvm-shutdown-hooks + (let [config (-> (jruby-testutils/jruby-puppet-tk-config + (jruby-testutils/jruby-puppet-config {:max-active-instances 2 + :borrow-timeout + 12})) + (assoc :webserver {:port 8081 + :shutdown-timeout-seconds 1})) + service (tk-app/get-service app :PuppetServerConfigService) + service-config (get-config service) + merged-config (merge service-config {:certificate-authority + {}}) + settings (ca/config->ca-settings merged-config) + app (tk/boot-services-with-config + (service-and-deps-with-mock-jruby config) + config)] + (is (= false (:allow-auto-renewal settings))) + (is (= 7776000 (:auto-renewal-cert-ttl settings))) + (tk-app/stop app))))))) + (deftest multi-webserver-setting-override (let [webserver-config {:ssl-cert "thehostcert" :ssl-key "thehostprivkey" @@ -105,8 +161,7 @@ :ssl-crl-path "thecacrl" :port 8081 :default-server true}] - (testing (str "webserver settings not overridden when mult-webserver config is provided" - "and full ssl cert configuration is available") + (testing "webserver settings not overridden when mult-webserver config is provided and full ssl cert configuration is available" (with-test-logging (let [config (assoc required-config :webserver {:puppet-server webserver-config})] @@ -116,8 +171,7 @@ config (is (logged? #"Not overriding webserver settings with values from core Puppet")))))) - (testing (str "webserver settings not overridden when single webserver is provided" - "and full ssl cert configuration is available") + (testing "webserver settings not overridden when single webserver is provided and full ssl cert configuration is available" (let [config (assoc required-config :webserver webserver-config)] (with-test-logging (tk-testutils/with-app-with-config diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/jruby_puppet_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,14 +1,11 @@ (ns puppetlabs.services.jruby.jruby-puppet-core-test - (:require [clojure.test :refer :all] - [me.raynes.fs :as fs] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [schema.test :as schema-test] [puppetlabs.kitchensink.core :as ks] [puppetlabs.services.jruby.jruby-puppet-core :as jruby-puppet-core] [puppetlabs.services.jruby-pool-manager.jruby-core :as jruby-core] - [puppetlabs.services.jruby-pool-manager.jruby-schemas :as jruby-schemas] [puppetlabs.trapperkeeper.testutils.logging :as logutils]) - (:import (java.io ByteArrayOutputStream PrintStream) - (org.jruby.runtime Constants))) + (:import (java.io ByteArrayOutputStream PrintStream))) (use-fixtures :once schema-test/validate-schemas) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/jruby_puppet_service_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/jruby_puppet_service_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/jruby_puppet_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/jruby_puppet_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,12 +1,9 @@ (ns puppetlabs.services.jruby.jruby-puppet-service-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.services.protocols.jruby-puppet :as jruby-protocol] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] - [puppetlabs.kitchensink.core :as ks] [puppetlabs.trapperkeeper.app :as app] - [puppetlabs.services.jruby.jruby-puppet-core :as jruby-puppet-core] [puppetlabs.trapperkeeper.testutils.bootstrap :as bootstrap] - [me.raynes.fs :as fs] [schema.test :as schema-test])) (use-fixtures :once schema-test/validate-schemas) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/jruby_puppet_testutils.clj 2024-01-15 23:29:55.000000000 +0000 @@ -13,7 +13,7 @@ [puppetlabs.trapperkeeper.services.metrics.metrics-service :as metrics] [puppetlabs.services.jruby.jruby-puppet-service :as jruby-puppet] [puppetlabs.services.puppet-profiler.puppet-profiler-service :as profiler] - [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9-service] + [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10-service] [puppetlabs.services.jruby-pool-manager.jruby-pool-manager-service :as jruby-pool-manager] [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as scheduler-service] [puppetlabs.trapperkeeper.services.status.status-service :as status-service] @@ -46,7 +46,7 @@ metrics/metrics-service scheduler-service/scheduler-service status-service/status-service - jetty9-service/jetty9-service + jetty10-service/jetty10-service webrouting-service/webrouting-service]) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -316,7 +316,7 @@ (partial create-mock-jruby-puppet (fn [_] (JRubyPuppetResponse. - (Integer. status-code) + (int status-code) response-body response-content-type puppet-version))))) @@ -361,7 +361,7 @@ pool-manager-protocol/PoolManagerService [] (create-pool - [this jruby-config] + [_this jruby-config] (create-mock-pool jruby-config (partial mock-jruby-puppet-fn config)))))) (defn add-mock-jruby-pool-manager-service diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/puppet_environments_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/puppet_environments_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/jruby/puppet_environments_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/jruby/puppet_environments_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.services.jruby.puppet-environments-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [puppetlabs.services.jruby.puppet-environments :as puppet-env])) (defn contains-environment? diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/master/file_serving_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/master/file_serving_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/master/file_serving_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/master/file_serving_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,7 +1,10 @@ (ns puppetlabs.services.master.file-serving-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [me.raynes.fs :as fs] - [puppetlabs.services.master.file-serving :refer :all]) + [puppetlabs.services.master.file-serving :refer [get-project-root + find-project-file + get-project-modulepath + read-attributes]]) (:import java.nio.file.Paths)) (deftest bolt-projects-test diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/master/master_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/master/master_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/master/master_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/master/master_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,14 +1,23 @@ (ns puppetlabs.services.master.master-core-test (:require [cheshire.core :as json] [clojure.string :refer [split]] - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing use-fixtures]] [puppetlabs.comidi :as comidi] [puppetlabs.kitchensink.core :as ks] [puppetlabs.services.jruby-pool-manager.impl.jruby-pool-manager-core :as jruby-pool-manager-core] [puppetlabs.services.jruby-pool-manager.jruby-core :as jruby-core] - [puppetlabs.services.master.master-core :refer :all] + [puppetlabs.services.master.master-core :refer [root-routes + wrap-middleware + if-none-match-from-request + class-info-from-jruby->class-info-for-json + valid-static-file-path? + validate-memory-requirements! + meminfo-content + max-heap-size + task-file-uri-components + all-tasks-response!]] [puppetlabs.services.master.master-service :as master-service] [puppetlabs.services.protocols.jruby-puppet :as jruby] [puppetlabs.trapperkeeper.testutils.logging :as logging] @@ -42,7 +51,8 @@ true nil ["./dev-resources/puppetlabs/services/master/master_core_test/builtin_bolt_content"] - "./dev-resources/puppetlabs/services/master/master_core_test/bolt_projects") + "./dev-resources/puppetlabs/services/master/master_core_test/bolt_projects" + "test-certname") (comidi/routes->handler) (wrap-middleware identity puppet-version))) @@ -56,10 +66,12 @@ request (partial app-request app)] (is (= 200 (:status (request "/v3/environments")))) (is (= 200 (:status (request "/v3/catalog/bar?environment=environment1234")))) - (is (= 200 (:status (app (-> {:request-method :post - :uri "/v3/catalog/bar" - :content-type "application/x-www-form-urlencoded"} - (ring-mock/body "environment=environment1234")))))) + (let [response (app (-> {:request-method :post + :uri "/v3/catalog/bar" + :content-type "application/x-www-form-urlencoded"} + (ring-mock/body "environment=environment1234")))] + (is (= "test-certname" (get-in response [:headers "X-Puppet-Compiler-Name"])) + (is (= 200 (:status response))))) (is (nil? (request "/foo"))) (is (nil? (request "/foo/bar"))) (doseq [[method paths] @@ -106,7 +118,7 @@ :gem-path "bar:foobar" :ruby-load-path ["foo"]}))) (get-environment-class-info [_ _ env] - (if (= env "production") + (when (= env "production") {})) (get-cached-content-version [_ _ _]) @@ -273,8 +285,7 @@ (is (every? (complement get-validity) invalid-path-results) (ks/pprint-to-string invalid-path-results))))) (deftest file-bucket-file-content-type-test - (testing (str "The 'Content-Type' header on incoming /file_bucket_file requests " - "is not overwritten, and simply passed through unmodified.") + (testing "The 'Content-Type' header on incoming /file_bucket_file requests is not overwritten, and simply passed through unmodified." (let [handler (fn ([req] {:request req})) app (build-ring-handler handler "1.2.3" dummy-jruby-service) resp (app (-> {:request-method :put @@ -284,11 +295,10 @@ (is (= "application/octet-stream" (get-in resp [:request :content-type]))) - (testing "Even if the client sends something insane, " - "just pass it through and let the puppet code handle it." + (testing "Even if the client sends something insane, just pass it through and let the puppet code handle it." (let [resp (app (-> {:request-method :put - :content-type "something-crazy/for-content-type" - :uri "/v3/file_bucket_file/bar"} + :content-type "something-crazy/for-content-type" + :uri "/v3/file_bucket_file/bar"} (ring-mock/body "foo")))] (is (= "something-crazy/for-content-type" (get-in resp [:request :content-type])))))))) @@ -367,7 +377,7 @@ :gem-path "bar:foobar" :ruby-load-path ["foo"]}))) (get-tasks [_ _ env] - (if (= env "production") + (when (= env "production") []))) handler (fn ([req] {:request req})) app (build-ring-handler handler "1.2.3" jruby-service) @@ -375,8 +385,7 @@ response (fn [info] (all-tasks-response! info - "production" - jruby-service)) + "production")) response-format (fn [task-name] {:name task-name :environment [{:name "production" diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -4,8 +4,8 @@ (ch.qos.logback.classic Logger Level) (com.puppetlabs.puppetserver PuppetProfiler) (com.codahale.metrics MetricRegistry Timer)) - (:require [clojure.test :refer :all] - [puppetlabs.services.puppet-profiler.puppet-profiler-core :refer :all] + (:require [clojure.test :refer [deftest is testing use-fixtures]] + [puppetlabs.services.puppet-profiler.puppet-profiler-core :refer [initialize metrics-profiler v1-status]] [puppetlabs.trapperkeeper.testutils.logging :as logutils] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.services.jruby-pool-manager.jruby-core :as jruby-core] diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/puppet_profiler/puppet_profiler_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,8 +1,8 @@ (ns puppetlabs.services.puppet-profiler.puppet-profiler-service-test (:import (com.puppetlabs.puppetserver PuppetProfiler)) - (:require [clojure.test :refer :all] - [puppetlabs.services.puppet-profiler.puppet-profiler-service :refer :all] - [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9-service] + (:require [clojure.test :refer [deftest is testing]] + [puppetlabs.services.puppet-profiler.puppet-profiler-service :refer [puppet-profiler-service]] + [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10-service] [puppetlabs.trapperkeeper.services.metrics.metrics-service :as metrics-service] [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as scheduler-service] [puppetlabs.trapperkeeper.services.status.status-service :as status-service] @@ -16,7 +16,7 @@ (bootstrap/with-app-with-config app [puppet-profiler-service - jetty9-service/jetty9-service + jetty10-service/jetty10-service metrics-service/metrics-service scheduler-service/scheduler-service status-service/status-service diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/request_handler/request_handler_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,7 +1,7 @@ (ns puppetlabs.services.request-handler.request-handler-core-test (:import (java.io StringReader ByteArrayInputStream) (com.puppetlabs.puppetserver JRubyPuppetResponse)) - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [slingshot.test :refer :all] [ring.util.codec :as ring-codec] [puppetlabs.ring-middleware.utils :as ringutils] @@ -11,12 +11,12 @@ [puppetlabs.trapperkeeper.testutils.logging :as logutils] [puppetlabs.services.jruby.jruby-puppet-testutils :as jruby-testutils] [puppetlabs.trapperkeeper.app :as tk-app] - [puppetlabs.trapperkeeper.services :refer [defservice service]] + [puppetlabs.trapperkeeper.services :refer [service]] [puppetlabs.services.jruby.jruby-puppet-service :as jruby-service] [puppetlabs.puppetserver.bootstrap-testutils :as jruby-bootstrap] [puppetlabs.services.protocols.versioned-code :as vc] [puppetlabs.services.puppet-profiler.puppet-profiler-service :as profiler] - [puppetlabs.trapperkeeper.services.webserver.jetty9-service :as jetty9] + [puppetlabs.trapperkeeper.services.webserver.jetty10-service :as jetty10] [puppetlabs.trapperkeeper.services.scheduler.scheduler-service :as tk-scheduler] [puppetlabs.services.request-handler.request-handler-service :as handler-service] [puppetlabs.services.config.puppet-server-config-service :as ps-config] @@ -67,7 +67,7 @@ :content-type "text/plain"})] (is (= {} (:params wrapped-request)) "Unexpected params in wrapped request") - (is (= "" (:body wrapped-request)) + (is (= "" (slurp (:body wrapped-request))) "Unexpected body for jruby in wrapped request"))) (testing "get with query parameters returns expected values" (let [wrapped-request (core/wrap-params-for-jruby @@ -79,14 +79,14 @@ :bogus ""} (:params wrapped-request)) "Unexpected params in wrapped request") - (is (= "" (:body wrapped-request)) + (is (= "" (slurp (:body wrapped-request))) "Unexpected body for jruby in wrapped request"))) (testing "post with form parameters returns expected values" (let [body-string "one=1&two=2%202&arr[]=3&arr[]=4" wrapped-request (core/wrap-params-for-jruby - {:body (StringReader. body-string) - :content-type "application/x-www-form-urlencoded" - :params {:bogus ""}})] + {:body (StringReader. body-string) + :content-type "application/x-www-form-urlencoded" + :params {:bogus ""}})] (is (= {"one" "1", "two" "2 2", "arr[]" ["3" "4"] :bogus ""} (:params wrapped-request)) @@ -101,7 +101,7 @@ :params {:bogus ""}})] (is (= {:bogus ""} (:params wrapped-request)) "Unexpected params in wrapped request") - (is (= body-string (:body wrapped-request)) + (is (= body-string (slurp (:body wrapped-request))) "Unexpected body for jruby in wrapped request"))) (testing "post with plain text in UTF-16 returns expected values" (let [body-string-from-utf16 (String. (.getBytes @@ -117,7 +117,7 @@ :params {:bogus ""}})] (is (= {:bogus ""} (:params wrapped-request)) "Unexpected params in wrapped request") - (is (= body-string-from-utf16 (:body wrapped-request)) + (is (= body-string-from-utf16 (slurp (:body wrapped-request) :encoding "UTF-16")) "Unexpected body for jruby in wrapped request"))) (testing "request with binary content type does not consume body" (let [body-string "some random text"] @@ -174,120 +174,121 @@ (get req :client-cert)))))))))) (deftest cert-info-in-headers - "In the case where Puppet Server is running under HTTP with an upstream HTTPS - terminator, the cert's CN, cert, and authenticated status will be provided as - HTTP headers. If cert info is not provided in the headers but is available - via SSL, the SSL info will be used." - (logutils/with-test-logging - (let [single-cert-url-encoded (-> (str test-resources-dir "/localhost.pem") - slurp - ring-codec/url-encode) - second-cert-url-encoded (-> (str test-resources-dir "/master.pem") - slurp - ring-codec/url-encode)] - - (testing "providing headers but not the puppet server config won't work." - (let [req (core/as-jruby-request - (puppetserver-config false) - {:request-method :get - :headers {"x-client-verify" "SUCCESS" - "x-client-dn" "CN=puppet" - "x-client-cert" single-cert-url-encoded}})] - (is (not (get req :authenticated))) - (is (nil? (get req :client-cert-cn))) - (is (nil? (get req :client-cert))))) - - (testing "providing headers and allow-header-cert-info to true works" - (let [req (core/as-jruby-request - (puppetserver-config true) - {:request-method :get - :headers {"x-client-verify" "SUCCESS" - "x-client-dn" "CN=puppet" - "x-client-cert" single-cert-url-encoded}})] - (is (get req :authenticated)) - (is (= "puppet" (get req :client-cert-cn))) - (is (= "CN=localhost" - (ssl-utils/get-subject-from-x509-certificate - (get req :client-cert)))))) - - (testing "a malformed DN string fails" - (let [req (core/as-jruby-request - (puppetserver-config true) - {:request-method :get - :headers {"x-client-verify" "SUCCESS" - "x-client-dn" "invalid-dn"}})] - (is (not (get req :authenticated))) - (is (nil? (get req :client-cert))) - (is (nil? (get req :client-cert-cn))))) - - (testing "Setting the auth header to something other than 'SUCCESS' fails" - (let [req (core/as-jruby-request - (puppetserver-config true) - {:request-method :get - :headers {"x-client-verify" "fail" - "x-client-dn" "CN=puppet"}})] - (is (not (get req :authenticated))) - (is (= "puppet" (get req :client-cert-cn))) - (is (nil? (get req :client-cert))))) - - (testing "cert and cn from header used and not from SSL cert when allow-header-cert-info true" - (let [cert (ssl-utils/pem->cert - (str test-resources-dir "/localhost.pem")) - req (core/as-jruby-request - (puppetserver-config true) - {:request-method :get - :ssl-client-cert cert - :headers {"x-client-verify" "SUCCESS" - "x-client-dn" "CN=puppet" - "x-client-cert" second-cert-url-encoded}})] - (is (get req :authenticated)) - (is (= "puppet" (get req :client-cert-cn))) - (is (= "CN=master1.example.org" - (ssl-utils/get-subject-from-x509-certificate - (get req :client-cert)))))) - - (testing "cert and cn from ssl used when allow-header-cert-info false" - (let [cert (ssl-utils/pem->cert - (str test-resources-dir "/localhost.pem")) - req (core/as-jruby-request - (puppetserver-config false) - {:request-method :get - :ssl-client-cert cert - :headers {"x-client-verify" "SUCCESS" - "x-client-dn" "CN=puppet" - "x-client-cert" second-cert-url-encoded}})] - (is (get req :authenticated)) - (is (= "localhost" (get req :client-cert-cn))) - (is (identical? cert (get req :client-cert)))))))) + (testing + "In the case where Puppet Server is running under HTTP with an upstream HTTPS + terminator, the cert's CN, cert, and authenticated status will be provided as + HTTP headers. If cert info is not provided in the headers but is available + via SSL, the SSL info will be used." + (logutils/with-test-logging + (let [single-cert-url-encoded (-> (str test-resources-dir "/localhost.pem") + slurp + ring-codec/url-encode) + second-cert-url-encoded (-> (str test-resources-dir "/master.pem") + slurp + ring-codec/url-encode)] + + (testing "providing headers but not the puppet server config won't work." + (let [req (core/as-jruby-request + (puppetserver-config false) + {:request-method :get + :headers {"x-client-verify" "SUCCESS" + "x-client-dn" "CN=puppet" + "x-client-cert" single-cert-url-encoded}})] + (is (not (get req :authenticated))) + (is (nil? (get req :client-cert-cn))) + (is (nil? (get req :client-cert))))) + + (testing "providing headers and allow-header-cert-info to true works" + (let [req (core/as-jruby-request + (puppetserver-config true) + {:request-method :get + :headers {"x-client-verify" "SUCCESS" + "x-client-dn" "CN=puppet" + "x-client-cert" single-cert-url-encoded}})] + (is (get req :authenticated)) + (is (= "puppet" (get req :client-cert-cn))) + (is (= "CN=localhost" + (ssl-utils/get-subject-from-x509-certificate + (get req :client-cert)))))) + + (testing "a malformed DN string fails" + (let [req (core/as-jruby-request + (puppetserver-config true) + {:request-method :get + :headers {"x-client-verify" "SUCCESS" + "x-client-dn" "invalid-dn"}})] + (is (not (get req :authenticated))) + (is (nil? (get req :client-cert))) + (is (nil? (get req :client-cert-cn))))) + + (testing "Setting the auth header to something other than 'SUCCESS' fails" + (let [req (core/as-jruby-request + (puppetserver-config true) + {:request-method :get + :headers {"x-client-verify" "fail" + "x-client-dn" "CN=puppet"}})] + (is (not (get req :authenticated))) + (is (= "puppet" (get req :client-cert-cn))) + (is (nil? (get req :client-cert))))) + + (testing "cert and cn from header used and not from SSL cert when allow-header-cert-info true" + (let [cert (ssl-utils/pem->cert + (str test-resources-dir "/localhost.pem")) + req (core/as-jruby-request + (puppetserver-config true) + {:request-method :get + :ssl-client-cert cert + :headers {"x-client-verify" "SUCCESS" + "x-client-dn" "CN=puppet" + "x-client-cert" second-cert-url-encoded}})] + (is (get req :authenticated)) + (is (= "puppet" (get req :client-cert-cn))) + (is (= "CN=master1.example.org" + (ssl-utils/get-subject-from-x509-certificate + (get req :client-cert)))))) + + (testing "cert and cn from ssl used when allow-header-cert-info false" + (let [cert (ssl-utils/pem->cert + (str test-resources-dir "/localhost.pem")) + req (core/as-jruby-request + (puppetserver-config false) + {:request-method :get + :ssl-client-cert cert + :headers {"x-client-verify" "SUCCESS" + "x-client-dn" "CN=puppet" + "x-client-cert" second-cert-url-encoded}})] + (is (get req :authenticated)) + (is (= "localhost" (get req :client-cert-cn))) + (is (identical? cert (get req :client-cert))))))))) (deftest cert-decoding-failures - "A cert provided in the x-client-cert header that cannot be decoded into - an X509Certificate object throws the expected failure" - (testing "Improperly URL encoded content" - (is (thrown+? #(and - (= (:kind %) :bad-request) - (re-matches - #"Unable to URL decode the x-client-cert header: For input string: \"1Q\".*" - (:msg %))) - (jruby-request-with-client-cert-header "%1Q%2")))) - (testing "Bad certificate content" - (is (thrown+? [:kind :bad-request - :msg (str "Unable to parse x-client-cert into " - "certificate: -----END CERTIFICATE not found")] - (jruby-request-with-client-cert-header - "-----BEGIN%20CERTIFICATE-----%0AM")))) - (testing "No certificate in content" - (is (thrown+? [:kind :bad-request - :msg "No certs found in PEM read from x-client-cert"] - (jruby-request-with-client-cert-header - "NOCERTSHERE")))) - (testing "More than 1 certificate in content" - (is (thrown+? [:kind :bad-request - :msg "Only 1 PEM should be supplied for x-client-cert but 3 found"] - (jruby-request-with-client-cert-header - (-> (str test-resources-dir "/master-with-all-cas.pem") - slurp - ring-codec/url-encode)))))) + (testing "A cert provided in the x-client-cert header that cannot be decoded into + an X509Certificate object throws the expected failure" + (testing "Improperly URL encoded content" + (is (thrown+? #(and + (= (:kind %) :bad-request) + (re-matches + #"Unable to URL decode the x-client-cert header: For input string: \"1Q\".*" + (:msg %))) + (jruby-request-with-client-cert-header "%1Q%2")))) + (testing "Bad certificate content" + (is (thrown+? [:kind :bad-request + :msg (str "Unable to parse x-client-cert into " + "certificate: -----END CERTIFICATE not found")] + (jruby-request-with-client-cert-header + "-----BEGIN%20CERTIFICATE-----%0AM")))) + (testing "No certificate in content" + (is (thrown+? [:kind :bad-request + :msg "No certs found in PEM read from x-client-cert"] + (jruby-request-with-client-cert-header + "NOCERTSHERE")))) + (testing "More than 1 certificate in content" + (is (thrown+? [:kind :bad-request + :msg "Only 1 PEM should be supplied for x-client-cert but 3 found"] + (jruby-request-with-client-cert-header + (-> (str test-resources-dir "/master-with-all-cas.pem") + slurp + ring-codec/url-encode))))))) (deftest ^:integration test-jruby-pool-not-full-during-code-id-generation (testing "A jruby instance is held while code id is generated" @@ -324,7 +325,7 @@ profiler/puppet-profiler-service handler-service/request-handler-service ps-config/puppet-server-config-service - jetty9/jetty9-service + jetty10/jetty10-service ca-service/certificate-authority-service authorization-service/authorization-service routing-service/webrouting-service @@ -342,7 +343,7 @@ app services {:jruby-puppet {:max-active-instances 1}} (partial jruby-testutils/create-mock-jruby-puppet (fn [request] - (JRubyPuppetResponse. (Integer. 200) + (JRubyPuppetResponse. (int 200) (cheshire/generate-string {"classes" [] "environment" "production" diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/versioned_code_service/versioned_code_core_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/versioned_code_service/versioned_code_core_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/versioned_code_service/versioned_code_core_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/versioned_code_service/versioned_code_core_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,9 +1,9 @@ (ns puppetlabs.services.versioned-code-service.versioned-code-core-test (:require - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing]] [puppetlabs.puppetserver.common :refer [CodeId]] [puppetlabs.services.versioned-code-service.versioned-code-core :as vc-core] - [puppetlabs.trapperkeeper.testutils.logging :as logging] + [puppetlabs.trapperkeeper.testutils.logging :refer [logged?] :as logging] [puppetlabs.kitchensink.core :as ks] [schema.core :as schema]) (:import (org.apache.commons.io IOUtils))) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/services/versioned_code_service/versioned_code_service_test.clj puppetserver-8.4.0/test/unit/puppetlabs/services/versioned_code_service/versioned_code_service_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/services/versioned_code_service/versioned_code_service_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/services/versioned_code_service/versioned_code_service_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,11 +1,11 @@ (ns puppetlabs.services.versioned-code-service.versioned-code-service-test (:require - [clojure.test :refer :all] + [clojure.test :refer [deftest is testing]] [puppetlabs.services.versioned-code-service.versioned-code-service :as vcs] [puppetlabs.services.protocols.versioned-code :as vc] [puppetlabs.trapperkeeper.testutils.bootstrap :as tk-testutils] [puppetlabs.trapperkeeper.app :as tk-app] - [puppetlabs.trapperkeeper.testutils.logging :as logging] + [puppetlabs.trapperkeeper.testutils.logging :refer [logged?] :as logging] [puppetlabs.kitchensink.core :as ks]) (:import (org.apache.commons.io IOUtils))) @@ -81,4 +81,4 @@ [vcs/versioned-code-service] {:versioned-code {:code-content-command (script-path "echo")}}) (catch IllegalStateException e - (is (re-find #"Only one of .* was set." (.getMessage e)))))))) \ No newline at end of file + (is (re-find #"Only one of .* was set." (.getMessage e)))))))) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/testutils/task_coordinator.clj puppetserver-8.4.0/test/unit/puppetlabs/testutils/task_coordinator.clj --- puppetserver-7.9.5/test/unit/puppetlabs/testutils/task_coordinator.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/testutils/task_coordinator.clj 2024-01-15 23:29:55.000000000 +0000 @@ -89,13 +89,15 @@ (advance-task-to coordinator task-id phase)) (log/debug "Done initializing task:" task-id))) +(def Scalar (schema/cond-pre schema/Str schema/Int schema/Keyword)) + (schema/defn ^:always-validate do-initialize-task ([coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar req-fn :- IFn] (do-initialize-task coordinator task-id req-fn nil)) ([coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar req-fn :- IFn phase :- (schema/maybe schema/Keyword)] (do-initialize-task* coordinator (str task-id) req-fn phase))) @@ -114,7 +116,7 @@ (schema/defn ^:always-validate do-notify-task-progress [coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar phase :- schema/Keyword] (do-notify-task-progress* coordinator (str task-id) phase)) @@ -148,7 +150,7 @@ (schema/defn ^:always-validate do-advance-task-to [coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar desired-phase :- schema/Keyword] (do-advance-task-to* coordinator (str task-id) desired-phase)) @@ -164,16 +166,16 @@ (schema/defn ^:always-validate do-advance-task-through-all-phases [coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword)] + task-id :- Scalar] (do-advance-task-through-all-phases* coordinator (str task-id))) (schema/defn ^:always-validate do-unblock-task-to ([coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar desired-phase :- schema/Keyword] (do-unblock-task-to coordinator task-id desired-phase nil)) ([coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar desired-phase :- schema/Keyword callback :- (schema/maybe IFn)] (log/debug "Unblocking task to phase:" task-id desired-phase) @@ -192,13 +194,13 @@ (schema/defn ^:always-validate do-wait-for-task [coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar desired-phase :- schema/Keyword] (do-wait-for-task* coordinator (str task-id) desired-phase)) (schema/defn ^:always-validate do-callback-at-phase [coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword) + task-id :- Scalar desired-phase :- schema/Keyword callback :- IFn] (async/go (do-wait-for-task coordinator task-id desired-phase) @@ -220,7 +222,7 @@ (schema/defn ^:always-validate do-final-result [coordinator :- (schema/protocol TaskCoordinator) - task-id :- (schema/either schema/Str schema/Int schema/Keyword)] + task-id :- Scalar] (do-final-result* coordinator (str task-id))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -245,5 +247,5 @@ ;; It might be better to break this into a record/type plus a protocol; ;; Then we could hide things like the `tasks` and `task-phases` in ;; the type and not expose as part of the protocol API. - (task-phases [this] task-phases) - (tasks [this] tasks)))) + (task-phases [_this] task-phases) + (tasks [_this] tasks)))) diff -Nru puppetserver-7.9.5/test/unit/puppetlabs/testutils/task_coordinator_test.clj puppetserver-8.4.0/test/unit/puppetlabs/testutils/task_coordinator_test.clj --- puppetserver-7.9.5/test/unit/puppetlabs/testutils/task_coordinator_test.clj 2023-02-07 19:03:08.000000000 +0000 +++ puppetserver-8.4.0/test/unit/puppetlabs/testutils/task_coordinator_test.clj 2024-01-15 23:29:55.000000000 +0000 @@ -1,5 +1,5 @@ (ns puppetlabs.testutils.task-coordinator-test - (:require [clojure.test :refer :all] + (:require [clojure.test :refer [deftest is testing]] [puppetlabs.testutils.task-coordinator :as tc])) (deftest task-coordinator-basic-test @@ -62,7 +62,7 @@ (partial task-fn :task1 task1-current-phase)) (tc/callback-at-phase coordinator :task1 :phase1 - (fn [task-id phase] + (fn [_task-id _phase] (deliver callback-promise true))) (tc/unblock-task-to coordinator :task1 :phase2)