├── .rspec ├── spec ├── spec.opts ├── support │ ├── aio.rb │ └── acceptance │ │ └── puppetserver.rb ├── setup_acceptance_node.pp ├── acceptance │ ├── hieradata │ │ └── common.yaml │ ├── puppet_spec.rb │ ├── puppetserver_latest_spec.rb │ └── puppetserver_upgrade_spec.rb ├── classes │ ├── puppet_server_service_spec.rb │ ├── puppet_server_puppetdb_spec.rb │ ├── puppet_init_spec.rb │ ├── puppet_config_spec.rb │ ├── puppet_agent_spec.rb │ └── puppet_server_spec.rb ├── spec_helper_acceptance.rb ├── spec_helper.rb └── defines │ └── puppet_config_entry_spec.rb ├── .puppet-lint.rc ├── .sync.yml ├── types └── custom_trusted_oid_mapping.pp ├── templates ├── server │ ├── autosign.conf.erb │ ├── puppetserver │ │ ├── conf.d │ │ │ ├── product.conf.erb │ │ │ ├── ca.conf.erb │ │ │ ├── webserver.conf.erb │ │ │ ├── metrics.conf.erb │ │ │ ├── puppetserver.conf.erb │ │ │ └── auth.conf.erb │ │ └── services.d │ │ │ └── ca.cfg.erb │ └── post-receive.erb └── agent │ ├── systemd.puppet-run.service.erb │ └── systemd.puppet-run.timer.erb ├── manifests ├── server │ ├── enc.pp │ ├── service.pp │ ├── foreman.pp │ ├── puppetdb.pp │ ├── install.pp │ ├── config.pp │ └── puppetserver.pp ├── agent.pp ├── config │ ├── main.pp │ ├── agent.pp │ ├── server.pp │ └── entry.pp ├── agent │ ├── service │ │ ├── daemon.pp │ │ ├── cron.pp │ │ └── systemd.pp │ ├── install.pp │ ├── config.pp │ └── service.pp ├── config.pp └── params.pp ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .pmtignore ├── Gemfile ├── .fixtures.yml ├── Rakefile ├── metadata.json ├── README.md └── CONTRIBUTING.md /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --tty 4 | --backtrace 5 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --format 2 | documentation 3 | --colour 4 | mtime 5 | --backtrace 6 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --fail-on-warnings 2 | --no-140chars-check 3 | --no-class_inherits_from_params_class-check 4 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | .github/workflows/ci.yml: 3 | pidfile_workaround: true 4 | Gemfile: 5 | extra: 6 | - gem: hocon 7 | -------------------------------------------------------------------------------- /types/custom_trusted_oid_mapping.pp: -------------------------------------------------------------------------------- 1 | type Puppet::Custom_trusted_oid_mapping = Hash[String, Struct[{ shortname => String, longname => Optional[String], }]] 2 | -------------------------------------------------------------------------------- /templates/server/autosign.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | <% scope.lookupvar("puppet::server::autosign_entries").each do |entry| -%> 5 | <%= entry %> 6 | <% end -%> 7 | -------------------------------------------------------------------------------- /templates/server/puppetserver/conf.d/product.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | product: { 5 | # automatic update checks and corresponding analytic data collection 6 | check-for-updates: <%= @server_check_for_updates %> 7 | } 8 | -------------------------------------------------------------------------------- /templates/agent/systemd.puppet-run.service.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | [Unit] 5 | Description=Systemd Timer Service for Puppet Agent 6 | After=network.target 7 | 8 | [Service] 9 | Type=oneshot 10 | ExecStart=<%= @command %> 11 | SuccessExitStatus=2 12 | User=root 13 | Group=root 14 | -------------------------------------------------------------------------------- /spec/support/aio.rb: -------------------------------------------------------------------------------- 1 | add_custom_fact :aio_agent_version, ->(os, facts) do 2 | return facts[:puppetversion] unless ['Archlinux', 'FreeBSD', 'DragonFly', 'Windows'].include?(facts[:os]['family']) 3 | end 4 | 5 | def unsupported_puppetserver_osfamily(osfamily) 6 | ['Archlinux', 'windows', 'Suse'].include?(osfamily) 7 | end 8 | -------------------------------------------------------------------------------- /spec/setup_acceptance_node.pp: -------------------------------------------------------------------------------- 1 | $packages = $facts['os']['name'] ? { 2 | # iproute is needed for the ss command in testing and not included in the base container 3 | 'Fedora' => ['cronie', 'iproute'], 4 | 'Ubuntu' => ['cron'], 5 | default => [], 6 | } 7 | 8 | package { $packages: 9 | ensure => installed, 10 | } 11 | -------------------------------------------------------------------------------- /manifests/server/enc.pp: -------------------------------------------------------------------------------- 1 | # Set up the ENC config 2 | # @api private 3 | class puppet::server::enc ( 4 | Variant[Undef, String[0], Stdlib::Absolutepath] $enc_path = $puppet::server::external_nodes 5 | ) { 6 | puppet::config::server { 7 | 'external_nodes': value => $enc_path; 8 | 'node_terminus': value => 'exec'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /spec/acceptance/hieradata/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | puppet::server_foreman: false 3 | puppet::server_reports: 'store' 4 | puppet::server_external_nodes: '' 5 | # only for install test - don't think to use this in production! 6 | # https://docs.puppet.com/puppetserver/latest/tuning_guide.html 7 | puppet::server_jvm_max_heap_size: '768m' 8 | puppet::server_jvm_min_heap_size: '256m' 9 | -------------------------------------------------------------------------------- /manifests/agent.pp: -------------------------------------------------------------------------------- 1 | # Puppet agent 2 | # @api private 3 | class puppet::agent { 4 | contain puppet::agent::install 5 | contain puppet::agent::config 6 | contain puppet::agent::service 7 | 8 | Class['puppet::agent::install'] ~> Class['puppet::agent::config', 'puppet::agent::service'] 9 | Class['puppet::config', 'puppet::agent::config'] ~> Class['puppet::agent::service'] 10 | } 11 | -------------------------------------------------------------------------------- /templates/agent/systemd.puppet-run.timer.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | [Unit] 5 | Description=Systemd Timer for Puppet Agent 6 | 7 | [Timer] 8 | OnCalendar=*-*-* <%= Array(@_hour).join(',') %>:<%= Array(@_minute).join(',') %>:00<% if @timezone -%> <%= @timezone %><% end %> 9 | Persistent=true 10 | RandomizedDelaySec=<%= @randomizeddelaysec %> 11 | 12 | [Install] 13 | WantedBy=timers.target 14 | -------------------------------------------------------------------------------- /manifests/server/service.pp: -------------------------------------------------------------------------------- 1 | # Set up the puppet server as a service 2 | # 3 | # @param $enable Whether to enable the service or not 4 | # @param $service_name The service name to manage 5 | # 6 | # @api private 7 | class puppet::server::service ( 8 | Boolean $enable = true, 9 | String $service_name = 'puppetserver', 10 | ) { 11 | service { $service_name: 12 | ensure => $enable, 13 | enable => $enable, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | on: 5 | pull_request: 6 | push: 7 | branches: 8 | - 'develop' 9 | - 'master' 10 | - '*-stable' 11 | 12 | 13 | concurrency: 14 | group: ${{ github.ref_name }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | puppet: 19 | name: Puppet 20 | uses: voxpupuli/gha-puppet/.github/workflows/beaker.yml@v3 21 | with: 22 | pidfile_workaround: 'true' 23 | rubocop: false 24 | -------------------------------------------------------------------------------- /spec/acceptance/puppet_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'Scenario: install puppet' do 4 | before(:context) do 5 | on default, 'puppet resource service puppet ensure=stopped enable=false' 6 | end 7 | 8 | it_behaves_like 'an idempotent resource' do 9 | let(:manifest) { 'include puppet' } 10 | end 11 | 12 | describe service('puppet') do 13 | it { is_expected.to be_running } 14 | it { is_expected.to be_enabled } 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/classes/puppet_server_service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet::server::service' do 4 | context 'default parameters' do 5 | it { is_expected.to contain_service('puppetserver').with_ensure(true).with_enable(true) } 6 | end 7 | 8 | context 'enable => false' do 9 | let(:params) do 10 | { enable: false } 11 | end 12 | 13 | it { is_expected.to contain_service('puppetserver').with_ensure(false).with_enable(false) } 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/acceptance/puppetserver.rb: -------------------------------------------------------------------------------- 1 | def unsupported_puppetserver 2 | case host_inventory['facter']['os']['name'] 3 | when 'Archlinux' 4 | true 5 | when 'Fedora' 6 | true 7 | end 8 | end 9 | 10 | def unsupported_puppetserver_upgrade 11 | case host_inventory['facter']['os']['name'] 12 | when 'Archlinux' 13 | true 14 | when 'Fedora' 15 | true 16 | when 'Debian' 17 | ENV['BEAKER_PUPPET_COLLECTION'] == 'puppet8' && host_inventory['facter']['os']['release']['major'] == '12' 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /templates/server/puppetserver/services.d/ca.cfg.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | # To enable the CA service, leave the following line uncommented 5 | <%= '#' unless @server_ca -%>puppetlabs.services.ca.certificate-authority-service/certificate-authority-service 6 | # To disable the CA service, comment out the above line and uncomment the line below 7 | <%= '#' if @server_ca -%>puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service 8 | puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service 9 | -------------------------------------------------------------------------------- /manifests/config/main.pp: -------------------------------------------------------------------------------- 1 | # Set a config entry in the [main] section 2 | # 3 | # @param value 4 | # The value for the config entry 5 | # @param key 6 | # The key of the config entry 7 | # @param joiner 8 | # How to join an array value into a string 9 | define puppet::config::main ( 10 | Variant[Array[String], Boolean, String, Integer] $value, 11 | String $key = $name, 12 | String $joiner = ',' 13 | ) { 14 | puppet::config::entry { "main${name}": 15 | key => $key, 16 | value => $value, 17 | joiner => $joiner, 18 | section => 'main', 19 | sectionorder => 1, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /manifests/config/agent.pp: -------------------------------------------------------------------------------- 1 | # Set a config entry in the [agent] section 2 | # 3 | # @param value 4 | # The value for the config entry 5 | # @param key 6 | # The key of the config entry 7 | # @param joiner 8 | # How to join an array value into a string 9 | define puppet::config::agent ( 10 | Variant[Array[String], Boolean, String, Integer] $value, 11 | String $key = $name, 12 | String $joiner = ',' 13 | ) { 14 | puppet::config::entry { "agent_${name}": 15 | key => $key, 16 | value => $value, 17 | joiner => $joiner, 18 | section => 'agent', 19 | sectionorder => 2, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /manifests/config/server.pp: -------------------------------------------------------------------------------- 1 | # Set a config entry in the [server] section 2 | # 3 | # @param value 4 | # The value for the config entry 5 | # @param key 6 | # The key of the config entry 7 | # @param joiner 8 | # How to join an array value into a string 9 | define puppet::config::server ( 10 | Variant[Array[String], Boolean, String, Integer] $value, 11 | String $key = $name, 12 | String $joiner = ',' 13 | ) { 14 | puppet::config::entry { "server_${name}": 15 | key => $key, 16 | value => $value, 17 | joiner => $joiner, 18 | section => 'server', 19 | sectionorder => 3, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by modulesync 2 | # https://github.com/theforeman/foreman-installer-modulesync 3 | 4 | ## MAC OS 5 | .DS_Store 6 | 7 | ## TEXTMATE 8 | *.tmproj 9 | tmtags 10 | 11 | ## EMACS 12 | *~ 13 | \#* 14 | .\#* 15 | 16 | ## VIM 17 | *.swp 18 | *.swo 19 | tags 20 | 21 | ## Bundler 22 | Gemfile.lock 23 | .bundle 24 | vendor/ 25 | 26 | ## rbenv / rvm 27 | .rbenv* 28 | .rvmrc* 29 | .ruby-* 30 | 31 | ## rspec 32 | spec/fixtures/manifests 33 | spec/fixtures/modules 34 | junit/ 35 | 36 | ## Puppet module 37 | pkg/ 38 | coverage/ 39 | .yardoc/ 40 | REFERENCE.md 41 | 42 | ## InteliJ / RubyMine 43 | .idea 44 | 45 | ## Beaker 46 | .vagrant/ 47 | log/ 48 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | require 'voxpupuli/acceptance/spec_helper_acceptance' 2 | 3 | ENV['BEAKER_setfile'] ||= 'centos8-64{hostname=centos8-64.example.com}' 4 | 5 | configure_beaker(modules: :fixtures) do |host| 6 | if fact_on(host, 'os.family') == 'RedHat' 7 | unless fact_on(host, 'os.name') == 'Fedora' 8 | # don't delete downloaded rpm for use with BEAKER_provision=no + 9 | # BEAKER_destroy=no 10 | on host, 'sed -i "s/keepcache=.*/keepcache=1/" /etc/yum.conf' 11 | end 12 | # refresh check if cache needs refresh on next yum command 13 | on host, 'yum clean expire-cache' 14 | end 15 | end 16 | 17 | Dir["./spec/support/acceptance/**/*.rb"].sort.each { |f| require f } 18 | -------------------------------------------------------------------------------- /.pmtignore: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by modulesync 2 | # https://github.com/theforeman/foreman-installer-modulesync 3 | 4 | ## MAC OS 5 | .DS_Store 6 | 7 | ## TEXTMATE 8 | *.tmproj 9 | tmtags 10 | 11 | ## EMACS 12 | *~ 13 | \#* 14 | .\#* 15 | 16 | ## VIM 17 | *.swp 18 | *.swo 19 | tags 20 | 21 | ## Bundler 22 | Gemfile.lock 23 | .bundle 24 | vendor/ 25 | 26 | ## rbenv / rvm 27 | .rbenv* 28 | .rvmrc* 29 | .ruby-* 30 | 31 | ## rspec 32 | spec/fixtures/ 33 | junit/ 34 | 35 | ## Puppet module 36 | pkg/ 37 | coverage/ 38 | .yardoc/ 39 | 40 | ## InteliJ / RubyMine 41 | .idea 42 | 43 | ## Beaker 44 | .vagrant/ 45 | log/ 46 | 47 | # Files we don't want to ship in a release 48 | CONTRIBUTING.md 49 | Gemfile 50 | Rakefile 51 | spec/ 52 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by modulesync 2 | # https://github.com/theforeman/foreman-installer-modulesync 3 | 4 | source 'https://rubygems.org' 5 | 6 | gem 'puppet', ENV.fetch('PUPPET_GEM_VERSION', '>= 8'), groups: ['development', 'test'] 7 | gem 'rake' 8 | 9 | gem 'kafo_module_lint', {"groups"=>["test"]} 10 | gem 'puppet-lint-spaceship_operator_without_tag-check', '~> 1.0', {"groups"=>["test"]} 11 | gem 'voxpupuli-test', '~> 9.0', {"groups"=>["test"]} 12 | gem 'github_changelog_generator', '>= 1.15.0', {"groups"=>["development"]} 13 | gem 'puppet_metadata', '~> 5.3' 14 | gem 'puppet-blacksmith', '>= 6.0.0', {"groups"=>["development"]} 15 | gem 'voxpupuli-acceptance', '~> 4.1', {"groups"=>["system_tests"]} 16 | gem 'puppetlabs_spec_helper', {"groups"=>["system_tests"]} 17 | gem 'hocon' 18 | 19 | # vim:ft=ruby 20 | -------------------------------------------------------------------------------- /manifests/agent/service/daemon.pp: -------------------------------------------------------------------------------- 1 | # Set up running the agent as a daemon 2 | # @api private 3 | class puppet::agent::service::daemon ( 4 | Boolean $enabled = false, 5 | ) { 6 | unless $puppet::runmode == 'unmanaged' or 'service' in $puppet::unavailable_runmodes { 7 | if $enabled { 8 | service { 'puppet': 9 | ensure => running, 10 | name => $puppet::service_name, 11 | hasstatus => true, 12 | hasrestart => $puppet::agent_restart_command != undef, 13 | enable => true, 14 | restart => $puppet::agent_restart_command, 15 | } 16 | } else { 17 | service { 'puppet': 18 | ensure => stopped, 19 | name => $puppet::service_name, 20 | hasstatus => true, 21 | enable => false, 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /templates/server/puppetserver/conf.d/ca.conf.erb: -------------------------------------------------------------------------------- 1 | certificate-authority: { 2 | # allow CA to sign certificate requests that have subject alternative names. 3 | allow-subject-alt-names: <%= @ca_allow_sans %> 4 | 5 | # allow CA to sign certificate requests that have authorization extensions. 6 | allow-authorization-extensions: <%= @ca_allow_auth_extensions %> 7 | 8 | # enable the separate CRL for Puppet infrastructure nodes 9 | enable-infra-crl: <%= @ca_enable_infra_crl %> 10 | 11 | # Setup auto renewal of certs 12 | allow-auto-renewal: <%= @server_ca_allow_auto_renewal %> 13 | # This value determines the lifetime of the cert if auto-renewal is enabled 14 | auto-renewal-cert-ttl: <%= @server_ca_allow_auto_renewal_cert_ttl %> 15 | # Default cert expiration time. If the value is set here, it will take precedence over ca-ttl setting in puppet.conf 16 | #ca-ttl: "60d" 17 | } 18 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | repositories: 3 | augeas_core: 'https://github.com/puppetlabs/puppetlabs-augeas_core' 4 | concat: 'https://github.com/puppetlabs/puppetlabs-concat.git' 5 | cron_core: 'https://github.com/puppetlabs/puppetlabs-cron_core' 6 | extlib: 'https://github.com/voxpupuli/puppet-extlib.git' 7 | git: 'https://github.com/theforeman/puppet-git.git' 8 | inifile: 'https://github.com/puppetlabs/puppetlabs-inifile.git' 9 | puppetdb: 'https://github.com/puppetlabs/puppetlabs-puppetdb.git' 10 | puppetserver_foreman: 'https://github.com/theforeman/puppet-puppetserver_foreman.git' 11 | stdlib: 'https://github.com/puppetlabs/puppetlabs-stdlib.git' 12 | systemd: 'https://github.com/voxpupuli/puppet-systemd.git' 13 | vcsrepo: 'https://github.com/puppetlabs/puppetlabs-vcsrepo.git' 14 | -------------------------------------------------------------------------------- /manifests/agent/install.pp: -------------------------------------------------------------------------------- 1 | # Install the puppet agent package 2 | # @api private 3 | class puppet::agent::install ( 4 | Variant[Boolean, Enum['server', 'agent']] $manage_packages = $puppet::manage_packages, 5 | Variant[String, Array[String]] $package_name = $puppet::client_package, 6 | String[1] $package_version = $puppet::version, 7 | Optional[String[1]] $package_provider = $puppet::package_provider, 8 | Variant[Undef, String, Hash, Array] $package_install_options = $puppet::package_install_options, 9 | Variant[Undef, Stdlib::Absolutepath, Stdlib::HTTPUrl] $package_source = $puppet::package_source, 10 | ) { 11 | if $manage_packages == true or $manage_packages == 'agent' { 12 | package { $package_name: 13 | ensure => $package_version, 14 | provider => $package_provider, 15 | install_options => $package_install_options, 16 | source => $package_source, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /manifests/agent/service/cron.pp: -------------------------------------------------------------------------------- 1 | # Set up running the agent via cron 2 | # @api private 3 | class puppet::agent::service::cron ( 4 | Boolean $enabled = false, 5 | Optional[Integer[0,23]] $hour = undef, 6 | Variant[Integer[0,59], Array[Integer[0,59]], Undef] $minute = undef, 7 | ) { 8 | unless $puppet::runmode == 'unmanaged' or 'cron' in $puppet::unavailable_runmodes { 9 | if $enabled { 10 | $command = pick($puppet::cron_cmd, "${puppet::puppet_cmd} agent --config ${puppet::dir}/puppet.conf --onetime --no-daemonize") 11 | $times = extlib::ip_to_cron($puppet::runinterval) 12 | 13 | $_hour = pick($hour, $times[0]) 14 | $_minute = pick($minute, $times[1]) 15 | 16 | cron { 'puppet': 17 | command => $command, 18 | user => root, 19 | hour => $_hour, 20 | minute => $_minute, 21 | } 22 | } else { 23 | cron { 'puppet': 24 | ensure => absent, 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spec/classes/puppet_server_puppetdb_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet::server::puppetdb' do 4 | on_supported_os.each do |os, os_facts| 5 | context "on #{os}", unless: unsupported_puppetserver_osfamily(os_facts[:os]['family']) do 6 | let(:facts) { os_facts } 7 | let(:params) { {server: 'mypuppetdb.example.com'} } 8 | let(:pre_condition) do 9 | <<-PUPPET 10 | class { 'puppet': 11 | server => true, 12 | server_reports => 'puppetdb,foreman', 13 | server_storeconfigs => true, 14 | } 15 | PUPPET 16 | end 17 | 18 | it { is_expected.to compile.with_all_deps } 19 | it { is_expected.to contain_puppet__config__server('storeconfigs').with_value(true) } 20 | it 'configures PuppetDB' do 21 | is_expected.to contain_class('puppetdb::master::config') 22 | .with_puppetdb_server('mypuppetdb.example.com') 23 | .with_puppetdb_port(8081) 24 | .with_puppetdb_soft_write_failure(false) 25 | .with_manage_storeconfigs(false) 26 | .with_restart_puppet(false) 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by modulesync 2 | # https://github.com/theforeman/foreman-installer-modulesync 3 | 4 | require 'voxpupuli/test/spec_helper' 5 | 6 | add_mocked_facts! 7 | 8 | def get_content(subject, title) 9 | is_expected.to contain_file(title) 10 | content = subject.resource('file', title).send(:parameters)[:content] 11 | content.split(/\n/).reject { |line| line =~ /(^#|^$|^\s+#)/ } 12 | end 13 | 14 | def verify_exact_contents(subject, title, expected_lines) 15 | expect(get_content(subject, title)).to match_array(expected_lines) 16 | end 17 | 18 | def verify_concat_fragment_contents(subject, title, expected_lines) 19 | is_expected.to contain_concat__fragment(title) 20 | content = subject.resource('concat::fragment', title).send(:parameters)[:content] 21 | expect(content.split("\n") & expected_lines).to match_array(expected_lines) 22 | end 23 | 24 | def verify_concat_fragment_exact_contents(subject, title, expected_lines) 25 | is_expected.to contain_concat__fragment(title) 26 | content = subject.resource('concat::fragment', title).send(:parameters)[:content] 27 | expect(content.split(/\n/).reject { |line| line =~ /(^#|^$|^\s+#)/ }).to match_array(expected_lines) 28 | end 29 | 30 | Dir["./spec/support/**/*.rb"].sort.each { |f| require f } 31 | -------------------------------------------------------------------------------- /manifests/server/foreman.pp: -------------------------------------------------------------------------------- 1 | # @summary Set up Foreman integration 2 | # @api private 3 | class puppet::server::foreman ( 4 | Boolean $katello = false, 5 | ) { 6 | if $katello { 7 | include certs::puppet 8 | Class['certs::puppet'] -> Class['puppetserver_foreman'] 9 | 10 | $ssl_ca = $certs::puppet::ssl_ca_cert 11 | $ssl_cert = $certs::puppet::client_cert 12 | $ssl_key = $certs::puppet::client_key 13 | } else { 14 | $ssl_ca = pick($puppet::server::foreman_ssl_ca, $puppet::server::ssl_ca_cert) 15 | $ssl_cert = pick($puppet::server::foreman_ssl_cert, $puppet::server::ssl_cert) 16 | $ssl_key = pick($puppet::server::foreman_ssl_key, $puppet::server::ssl_cert_key) 17 | } 18 | 19 | # Include foreman components for the puppetserver 20 | # ENC script, reporting script etc. 21 | class { 'puppetserver_foreman': 22 | foreman_url => $puppet::server::foreman_url, 23 | enc_upload_facts => $puppet::server::server_foreman_facts, 24 | enc_timeout => $puppet::server::request_timeout, 25 | puppet_home => $puppet::server::puppetserver_vardir, 26 | puppet_basedir => $puppet::server::puppet_basedir, 27 | puppet_etcdir => $puppet::dir, 28 | ssl_ca => $ssl_ca, 29 | ssl_cert => $ssl_cert, 30 | ssl_key => $ssl_key, 31 | } 32 | contain puppetserver_foreman 33 | } 34 | -------------------------------------------------------------------------------- /manifests/agent/service/systemd.pp: -------------------------------------------------------------------------------- 1 | # Set up running the agent via a systemd timer 2 | # @api private 3 | class puppet::agent::service::systemd ( 4 | Boolean $enabled = false, 5 | Optional[Integer[0,23]] $hour = undef, 6 | Variant[Integer[0,59], Array[Integer[0,59]], Undef] $minute = undef, 7 | Optional[String[1]] $timezone = undef, 8 | ) { 9 | unless $puppet::runmode == 'unmanaged' or 'systemd.timer' in $puppet::unavailable_runmodes { 10 | # Use the same times as for cron 11 | $times = extlib::ip_to_cron($puppet::runinterval) 12 | 13 | # But only if they are not explicitly specified 14 | $_hour = pick($hour, $times[0]) 15 | $_minute = pick($minute, $times[1]) 16 | 17 | $command = pick($puppet::systemd_cmd, "${puppet::puppet_cmd} agent --config ${puppet::dir}/puppet.conf --onetime --no-daemonize --detailed-exitcode --no-usecacheonfailure") 18 | $randomizeddelaysec = $puppet::systemd_randomizeddelaysec 19 | 20 | systemd::timer { "${puppet::systemd_unit_name}.timer": 21 | ensure => bool2str($enabled, 'present', 'absent'), 22 | active => $enabled, 23 | enable => $enabled, 24 | timer_content => template('puppet/agent/systemd.puppet-run.timer.erb'), 25 | service_content => template('puppet/agent/systemd.puppet-run.service.erb'), 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /manifests/server/puppetdb.pp: -------------------------------------------------------------------------------- 1 | # @summary PuppetDB integration 2 | # 3 | # This class relies on the puppetlabs/puppetdb and essentially wraps 4 | # puppetdb::master::config with the proper resource chaining. 5 | # 6 | # Note that this doesn't manage the server itself. 7 | # 8 | # @example simple PuppetDB Setup 9 | # class { 'puppet': 10 | # server => true, 11 | # server_reports => 'puppetdb,foreman', 12 | # server_storeconfigs => true, 13 | # } 14 | # class { 'puppet::server::puppetdb': 15 | # server => 'mypuppetdb.example.com', 16 | # } 17 | # 18 | # @param server 19 | # The PuppetDB server 20 | # 21 | # @param port 22 | # The PuppetDB port 23 | # 24 | # @param soft_write_failure 25 | # Whether to enable soft write failure 26 | # 27 | # @param terminus_package 28 | # The PuppetDB terminus package 29 | # 30 | class puppet::server::puppetdb ( 31 | Stdlib::Host $server = undef, 32 | Stdlib::Port $port = 8081, 33 | Boolean $soft_write_failure = false, 34 | Optional[String[1]] $terminus_package = undef, 35 | ) { 36 | class { 'puppetdb::master::config': 37 | puppetdb_server => $server, 38 | puppetdb_port => $port, 39 | puppetdb_soft_write_failure => $soft_write_failure, 40 | manage_storeconfigs => false, 41 | restart_puppet => false, 42 | terminus_package => $terminus_package, 43 | } 44 | Class['puppetdb::master::puppetdb_conf'] ~> Class['puppet::server::service'] 45 | } 46 | -------------------------------------------------------------------------------- /templates/server/puppetserver/conf.d/webserver.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | webserver: { 5 | access-log-config: <%= @server_puppetserver_dir %>/request-logging.xml 6 | client-auth: want 7 | <%- if @server_http -%> 8 | host: <%= @server_ip %> 9 | port: <%= @server_http_port %> 10 | <%- end -%> 11 | ssl-host: <%= @server_ip %> 12 | ssl-port: <%= @server_port %> 13 | ssl-cert: <%= @server_ssl_cert %> 14 | ssl-key: <%= @server_ssl_cert_key %> 15 | ssl-ca-cert: <%= @server_ssl_ca_cert %> 16 | <%- if @server_crl_enable -%> 17 | ssl-crl-path: <%= @server_ssl_ca_crl %> 18 | <%- end -%> 19 | <%- if @server_ca -%> 20 | ssl-cert-chain: <%= @server_ssl_chain %> 21 | <%- end -%> 22 | idle-timeout-milliseconds: <%= @server_web_idle_timeout %> 23 | ssl-protocols: [ 24 | <%- @server_ssl_protocols.each do |ssl_protocol| -%> 25 | <%= ssl_protocol %>, 26 | <%- end -%> 27 | ] 28 | cipher-suites: [ 29 | <%- @server_cipher_suites.each do |cipher_suite| -%> 30 | <%= cipher_suite %>, 31 | <%- end -%> 32 | ] 33 | <%- if @acceptor_threads -%> 34 | acceptor-threads: <%= @acceptor_threads %> 35 | <%- end -%> 36 | <%- if @selector_threads -%> 37 | selector-threads: <%= @selector_threads %> 38 | <%- end -%> 39 | <%- if @ssl_acceptor_threads -%> 40 | ssl-acceptor-threads: <%= @ssl_acceptor_threads %> 41 | <%- end -%> 42 | <%- if @ssl_selector_threads -%> 43 | ssl-selector-threads: <%= @ssl_selector_threads %> 44 | <%- end -%> 45 | <%- if @max_threads -%> 46 | max-threads: <%= @max_threads %> 47 | <%- end -%> 48 | } 49 | -------------------------------------------------------------------------------- /manifests/agent/config.pp: -------------------------------------------------------------------------------- 1 | # Puppet agent configuration 2 | # @api private 3 | class puppet::agent::config inherits puppet::config { 4 | puppet::config::agent { 5 | 'classfile': value => $puppet::classfile; 6 | 'default_schedules': value => $puppet::agent_default_schedules; 7 | 'report': value => $puppet::report; 8 | 'masterport': value => $puppet::agent_server_port; 9 | 'splay': value => $puppet::splay; 10 | 'splaylimit': value => $puppet::splaylimit; 11 | 'runinterval': value => $puppet::runinterval; 12 | 'noop': value => $puppet::agent_noop; 13 | 'usecacheonfailure': value => $puppet::usecacheonfailure; 14 | } 15 | if $puppet::agent_manage_environment { 16 | puppet::config::agent { 'environment': value => $puppet::environment } 17 | } 18 | if $puppet::http_connect_timeout != undef { 19 | puppet::config::agent { 20 | 'http_connect_timeout': value => $puppet::http_connect_timeout; 21 | } 22 | } 23 | if $puppet::http_read_timeout != undef { 24 | puppet::config::agent { 25 | 'http_read_timeout': value => $puppet::http_read_timeout; 26 | } 27 | } 28 | if $puppet::prerun_command { 29 | puppet::config::agent { 30 | 'prerun_command': value => $puppet::prerun_command; 31 | } 32 | } 33 | if $puppet::postrun_command { 34 | puppet::config::agent { 35 | 'postrun_command': value => $puppet::postrun_command; 36 | } 37 | } 38 | 39 | $puppet::agent_additional_settings.each |$key,$value| { 40 | puppet::config::agent { $key: value => $value } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /manifests/agent/service.pp: -------------------------------------------------------------------------------- 1 | # Set up the puppet agent as a service 2 | # @api private 3 | class puppet::agent::service { 4 | case $puppet::runmode { 5 | 'service': { 6 | $service_enabled = true 7 | $cron_enabled = false 8 | $systemd_enabled = false 9 | } 10 | 'cron': { 11 | $service_enabled = false 12 | $cron_enabled = true 13 | $systemd_enabled = false 14 | } 15 | 'systemd.timer': { 16 | $service_enabled = false 17 | $cron_enabled = false 18 | $systemd_enabled = true 19 | } 20 | 'none', 'unmanaged': { 21 | $service_enabled = false 22 | $cron_enabled = false 23 | $systemd_enabled = false 24 | } 25 | default: { 26 | fail("Runmode of ${puppet::runmode} not supported by puppet::agent::config!") 27 | } 28 | } 29 | 30 | if $puppet::runmode in $puppet::unavailable_runmodes { 31 | fail("Runmode of ${puppet::runmode} not supported on ${facts['kernel']} operating systems!") 32 | } 33 | 34 | class { 'puppet::agent::service::daemon': 35 | enabled => $service_enabled, 36 | } 37 | contain puppet::agent::service::daemon 38 | 39 | class { 'puppet::agent::service::systemd': 40 | enabled => $systemd_enabled, 41 | hour => $puppet::run_hour, 42 | minute => $puppet::run_minute, 43 | timezone => $puppet::run_timezone, 44 | } 45 | contain puppet::agent::service::systemd 46 | 47 | class { 'puppet::agent::service::cron': 48 | enabled => $cron_enabled, 49 | hour => $puppet::run_hour, 50 | minute => $puppet::run_minute, 51 | } 52 | contain puppet::agent::service::cron 53 | } 54 | -------------------------------------------------------------------------------- /manifests/config/entry.pp: -------------------------------------------------------------------------------- 1 | # Set a config entry 2 | # 3 | # @param key 4 | # The key of the config entry 5 | # @param value 6 | # The value for the config entry 7 | # @param section 8 | # The section for the config entry 9 | # @param sectionorder 10 | # How to order the section. This is only used on the first definition of the 11 | # section via ensure_resource. 12 | # @param joiner 13 | # How to join an array value into a string 14 | define puppet::config::entry ( 15 | String $key, 16 | Variant[Array[String], Boolean, String, Integer] $value, 17 | String $section, 18 | Variant[Integer[0], String] $sectionorder = 5, 19 | String $joiner = ',', 20 | ) { 21 | if ($value =~ Array) { 22 | $_value = join(flatten($value), $joiner) 23 | } elsif ($value =~ Boolean) { 24 | $_value = bool2str($value) 25 | } else { 26 | $_value = $value 27 | } 28 | 29 | # note the spaces at he end of the 'order' parameters, 30 | # they make sure that '1_main ' is ordered before '1_main_*' 31 | ensure_resource('concat::fragment', "puppet.conf_${section}", { 32 | target => "${puppet::dir}/puppet.conf", 33 | content => "\n[${section}]", 34 | order => "${sectionorder}_${section} ", 35 | }) 36 | ensure_resource('concat::fragment', "puppet.conf_${section}_end", { 37 | target => "${puppet::dir}/puppet.conf", 38 | content => "\n", 39 | order => "${sectionorder}_${section}~end", 40 | }) 41 | 42 | # this adds the '$key =' for the first value, 43 | # otherwise it just appends it with the joiner to separate it from the previous value. 44 | if (!defined(Concat::Fragment["puppet.conf_${section}_${key}"])) { 45 | concat::fragment { "puppet.conf_${section}_${key}": 46 | target => "${puppet::dir}/puppet.conf", 47 | content => "\n ${key} = ${_value}", 48 | order => "${sectionorder}_${section}_${key} ", 49 | } 50 | } else { 51 | concat::fragment { "puppet.conf_${section}_${key}_${name}": 52 | target => "${puppet::dir}/puppet.conf", 53 | content => "${joiner}${_value}", 54 | order => "${sectionorder}_${section}_${key}_${name} ", 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by modulesync 2 | # https://github.com/theforeman/foreman-installer-modulesync 3 | 4 | # Attempt to load voxupuli-test (which pulls in puppetlabs_spec_helper), 5 | # otherwise attempt to load it directly. 6 | begin 7 | require 'voxpupuli/test/rake' 8 | rescue LoadError 9 | begin 10 | require 'puppetlabs_spec_helper/rake_tasks' 11 | rescue LoadError 12 | end 13 | end 14 | 15 | # load optional tasks for acceptance 16 | # only available if gem group releases is installed 17 | begin 18 | require 'voxpupuli/acceptance/rake' 19 | rescue LoadError 20 | else 21 | # We use fixtures in our modules, which is not the default 22 | task :beaker => 'spec_prep' 23 | end 24 | 25 | # blacksmith isn't always present, e.g. on Travis with --without development 26 | begin 27 | require 'puppet_blacksmith/rake_tasks' 28 | Blacksmith::RakeTask.new do |t| 29 | t.tag_pattern = "%s" 30 | t.tag_message_pattern = "Version %s" 31 | t.tag_sign = true 32 | end 33 | rescue LoadError 34 | end 35 | 36 | begin 37 | require 'github_changelog_generator/task' 38 | 39 | # https://github.com/github-changelog-generator/github-changelog-generator/issues/313 40 | module GitHubChangelogGeneratorExtensions 41 | def compound_changelog 42 | super.gsub(/(fixes|fixing|refs) \\#(\d+)/i, '\1 [\\#\2](https://projects.theforeman.org/issues/\2)') 43 | end 44 | end 45 | 46 | class GitHubChangelogGenerator::Generator 47 | prepend GitHubChangelogGeneratorExtensions 48 | end 49 | 50 | GitHubChangelogGenerator::RakeTask.new :changelog do |config| 51 | raise "Set CHANGELOG_GITHUB_TOKEN environment variable eg 'export CHANGELOG_GITHUB_TOKEN=valid_token_here'" if Rake.application.top_level_tasks.include? "changelog" and ENV['CHANGELOG_GITHUB_TOKEN'].nil? 52 | metadata = JSON.load(File.read('metadata.json')) 53 | config.user = 'theforeman' 54 | config.project = "puppet-#{metadata['name'].split('-').last}" 55 | config.future_release = metadata['version'] 56 | config.exclude_labels = ['duplicate', 'question', 'invalid', 'wontfix', 'Modulesync', 'skip-changelog'] 57 | end 58 | rescue LoadError 59 | end 60 | -------------------------------------------------------------------------------- /manifests/server/install.pp: -------------------------------------------------------------------------------- 1 | # Install the puppet server 2 | # @api private 3 | class puppet::server::install { 4 | # Mirror the relationship, as defined() is parse-order dependent 5 | # Ensures 'puppet' user group is present before managing users 6 | if defined(Class['foreman_proxy::config']) { 7 | Class['puppet::server::install'] -> Class['foreman_proxy::config'] 8 | } 9 | if defined(Class['foreman::config']) { 10 | Class['puppet::server::install'] -> Class['foreman::config'] 11 | } 12 | 13 | if $puppet::server::git_repo { 14 | stdlib::ensure_packages(['git']) 15 | } 16 | 17 | if $puppet::server::manage_user { 18 | $shell = $puppet::server::git_repo ? { 19 | true => $facts['os']['family'] ? { 20 | /^(FreeBSD|DragonFly)$/ => '/usr/local/bin/git-shell', 21 | default => '/usr/bin/git-shell' 22 | }, 23 | default => undef, 24 | } 25 | 26 | user { $puppet::server::user: 27 | shell => $shell, 28 | } 29 | 30 | if $puppet::server::git_repo { 31 | Package['git'] -> User[$puppet::server::user] 32 | } 33 | } 34 | 35 | if $puppet::manage_packages == true or $puppet::manage_packages == 'server' { 36 | $server_package = $puppet::server::package 37 | $server_version = pick($puppet::server::version, $puppet::version) 38 | 39 | package { $server_package: 40 | ensure => $server_version, 41 | install_options => $puppet::package_install_options, 42 | } 43 | 44 | # Puppetserver 8 on EL 8 relies on JRE 11 or 17. This prefers JRE 17 by installing it first 45 | if ( 46 | !$puppet::server::jvm_java_bin and 47 | $facts['os']['family'] == 'RedHat' and $facts['os']['release']['major'] == '8' and 48 | # This doesn't use server_version because we have 2 mechanisms to set the version 49 | versioncmp(pick($puppet::server::puppetserver_version, $facts['puppetversion']), '8.0.0') >= 0 50 | ) { 51 | # EL 8 packaging can install either Java 17 or Java 11, but we prefer Java 17 52 | stdlib::ensure_packages(['jre-17-headless']) 53 | 54 | Package['jre-17-headless'] -> Package[$server_package] 55 | } 56 | 57 | if $puppet::server::manage_user { 58 | Package[$server_package] -> User[$puppet::server::user] 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /spec/acceptance/puppetserver_latest_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | describe 'Scenario: install puppetserver (latest):', unless: unsupported_puppetserver do 4 | before(:all) do 5 | if check_for_package(default, 'puppetserver') 6 | on default, 'puppet resource package puppetserver ensure=purged' 7 | on default, 'rm -rf /etc/sysconfig/puppetserver /etc/puppetlabs/puppetserver' 8 | on default, 'find /etc/puppetlabs/puppet/ssl/ -type f -delete' 9 | end 10 | 11 | # puppetserver won't start with lower than 2GB memory 12 | memoryfree_mb = fact('memoryfree_mb').to_i 13 | raise 'At least 2048MB free memory required' if memoryfree_mb < 256 14 | end 15 | 16 | context 'default options' do 17 | it_behaves_like 'an idempotent resource' do 18 | let(:manifest) do 19 | <<-EOS 20 | class { 'puppet': 21 | server => true, 22 | } 23 | EOS 24 | end 25 | end 26 | end 27 | 28 | if fact('os.family') == 'RedHat' 29 | describe 'JRE version' do 30 | it { expect(package('java-17-openjdk-headless')).to be_installed } 31 | it { expect(package('java-11-openjdk-headless')).not_to be_installed } 32 | it { expect(file('/etc/sysconfig/puppetserver')).to be_file.and(have_attributes(content: include('JAVA_BIN=/usr/lib/jvm/jre-17/bin/java'))) } 33 | end 34 | end 35 | 36 | # This is broken on Ubuntu Focal 37 | # https://github.com/theforeman/puppet-puppet/issues/832 38 | describe 'server_max_open_files', unless: unsupported_puppetserver || fact('os.release.major') == '20.04' do 39 | it_behaves_like 'an idempotent resource' do 40 | let(:manifest) do 41 | <<-MANIFEST 42 | class { 'puppet': 43 | server => true, 44 | server_max_open_files => 32143, 45 | } 46 | MANIFEST 47 | end 48 | end 49 | 50 | # pgrep -f java.*puppetserver would be better. But i cannot get it to work. Shellwords.escape() seems to break something 51 | describe command("grep '^Max open files' /proc/`cat /var/run/puppetlabs/puppetserver/puppetserver.pid`/limits"), :sudo => true do 52 | its(:exit_status) { is_expected.to eq 0 } 53 | its(:stdout) { is_expected.to match %r{^Max open files\s+32143\s+32143\s+files\s*$} } 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /templates/server/puppetserver/conf.d/metrics.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | # settings related to metrics 5 | metrics: { 6 | # a server id that will be used as part of the namespace for metrics produced 7 | # by this server 8 | server-id: "<%= @metrics_server_id %>" 9 | registries: { 10 | puppetserver: { 11 | # specify metrics to allow in addition to those in the default list 12 | <% if @metrics_allowed -%> 13 | metrics-allowed: [ 14 | <%- @metrics_allowed.each do |allowed_host| -%> 15 | "<%= allowed_host %>", 16 | <%- end -%> 17 | ] 18 | <%- else -%> 19 | #metrics-allowed: ["compiler.compile.production"] 20 | <% end -%> 21 | 22 | reporters: { 23 | # enable or disable JMX metrics reporter 24 | jmx: { 25 | enabled: <%= @metrics_jmx_enable %> 26 | } 27 | # enable or disable Graphite metrics reporter 28 | graphite: { 29 | enabled: <%= @metrics_graphite_enable %> 30 | } 31 | } 32 | 33 | } 34 | } 35 | 36 | # this section is used to configure settings for reporters that will send 37 | # the metrics to various destinations for external viewing 38 | reporters: { 39 | graphite: { 40 | # graphite host 41 | host: "<%= @metrics_graphite_host %>" 42 | # graphite metrics port 43 | port: <%= @metrics_graphite_port %> 44 | # how often to send metrics to graphite 45 | update-interval-seconds: <%= @metrics_graphite_interval %> 46 | } 47 | } 48 | metrics-webservice: { 49 | jolokia: { 50 | # Enable or disable the Jolokia-based metrics/v2 endpoint. 51 | # Default is true. 52 | # enabled: false 53 | 54 | # Configure any of the settings listed at: 55 | # https://jolokia.org/reference/html/agents.html#war-agent-installation 56 | servlet-init-params: { 57 | # Specify a custom security policy: 58 | # https://jolokia.org/reference/html/security.html 59 | # policyLocation: "file:///etc/puppetlabs/puppetserver/jolokia-access.xml" 60 | } 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "theforeman-puppet", 3 | "version": "22.0.1", 4 | "author": "theforeman", 5 | "summary": "Puppet agent and server configuration", 6 | "license": "GPL-3.0+", 7 | "source": "https://github.com/theforeman/puppet-puppet", 8 | "project_page": "https://github.com/theforeman/puppet-puppet", 9 | "issues_url": "https://github.com/theforeman/puppet-puppet/issues", 10 | "description": "Module for installing the Puppet agent and Puppet server", 11 | "tags": [ 12 | "foreman", 13 | "puppet", 14 | "puppetserver", 15 | "puppet-server" 16 | ], 17 | "dependencies": [ 18 | { 19 | "name": "puppetlabs/concat", 20 | "version_requirement": ">= 4.1.0 < 10.0.0" 21 | }, 22 | { 23 | "name": "puppetlabs/stdlib", 24 | "version_requirement": ">= 9.0.0 < 10.0.0" 25 | }, 26 | { 27 | "name": "puppet/extlib", 28 | "version_requirement": ">= 3.0.0 < 8.0.0" 29 | }, 30 | { 31 | "name": "puppet/systemd", 32 | "version_requirement": ">= 2.9.0 < 10.0.0" 33 | } 34 | ], 35 | "requirements": [ 36 | { 37 | "name": "puppet", 38 | "version_requirement": ">= 8.0.0 < 9.0.0" 39 | }, 40 | { 41 | "name": "openvox", 42 | "version_requirement": ">= 8.23.1 < 9.0.0" 43 | } 44 | ], 45 | "operatingsystem_support": [ 46 | { 47 | "operatingsystem": "RedHat", 48 | "operatingsystemrelease": [ 49 | "8", 50 | "9" 51 | ] 52 | }, 53 | { 54 | "operatingsystem": "CentOS", 55 | "operatingsystemrelease": [ 56 | "8", 57 | "9" 58 | ] 59 | }, 60 | { 61 | "operatingsystem": "OracleLinux", 62 | "operatingsystemrelease": [ 63 | "9" 64 | ] 65 | }, 66 | { 67 | "operatingsystem": "AlmaLinux", 68 | "operatingsystemrelease": [ 69 | "8", 70 | "9" 71 | ] 72 | }, 73 | { 74 | "operatingsystem": "Rocky", 75 | "operatingsystemrelease": [ 76 | "9" 77 | ] 78 | }, 79 | { 80 | "operatingsystem": "Fedora", 81 | "operatingsystemrelease": [ 82 | "36" 83 | ] 84 | }, 85 | { 86 | "operatingsystem": "Debian", 87 | "operatingsystemrelease": [ 88 | "11", 89 | "12" 90 | ] 91 | }, 92 | { 93 | "operatingsystem": "Ubuntu", 94 | "operatingsystemrelease": [ 95 | "20.04", 96 | "22.04" 97 | ] 98 | }, 99 | { 100 | "operatingsystem": "FreeBSD", 101 | "operatingsystemrelease": [ 102 | "11", 103 | "12" 104 | ] 105 | }, 106 | { 107 | "operatingsystem": "DragonFly", 108 | "operatingsystemrelease": [ 109 | "4" 110 | ] 111 | }, 112 | { 113 | "operatingsystem": "Archlinux" 114 | }, 115 | { 116 | "operatingsystem": "SLES", 117 | "operatingsystemrelease": [ 118 | "12" 119 | ] 120 | }, 121 | { 122 | "operatingsystem": "windows", 123 | "operatingsystemrelease": [ 124 | "2012", 125 | "2012 R2" 126 | ] 127 | } 128 | ] 129 | } 130 | -------------------------------------------------------------------------------- /spec/acceptance/puppetserver_upgrade_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper_acceptance' 2 | 3 | unless unsupported_puppetserver || unsupported_puppetserver_upgrade 4 | describe 'Scenario: minor version upgrade' do 5 | before(:all) do 6 | if check_for_package(default, 'puppetserver') 7 | on default, 'puppet resource package puppetserver ensure=purged' 8 | on default, 'rm -rf /etc/sysconfig/puppetserver /etc/puppetlabs/puppetserver' 9 | on default, 'rm -rf /etc/puppetlabs/puppet/ssl' 10 | end 11 | 12 | # puppetserver won't start with low memory 13 | memoryfree_mb = fact('memoryfree_mb').to_i 14 | raise 'At least 256MB free memory required' if memoryfree_mb < 256 15 | end 16 | 17 | # this is $implementation$majversion 18 | case ENV['BEAKER_PUPPET_COLLECTION'] 19 | when 'puppet8' 20 | from_version = '8.2.0' 21 | to_version = '8.5.0' 22 | when 'openvox8' 23 | from_version = '8.8.1' 24 | to_version = '8.11.0' 25 | else 26 | raise 'Unsupported Puppet collection' 27 | end 28 | 29 | case fact('os.family') 30 | when 'Debian' 31 | case ENV['BEAKER_PUPPET_COLLECTION'] 32 | when 'puppet8' 33 | from_version_exact = "#{from_version}-1#{fact('os.distro.codename')}" 34 | to_version_exact = "#{to_version}-1#{fact('os.distro.codename')}" 35 | when 'openvox8' 36 | from_version_exact = "#{from_version}-1+#{fact('os.name').downcase}#{fact('os.release.major')}" 37 | to_version_exact = "#{to_version}-1+#{fact('os.name').downcase}#{fact('os.release.major')}" 38 | end 39 | else 40 | from_version_exact = from_version 41 | to_version_exact = to_version 42 | end 43 | 44 | context "install #{from_version}" do 45 | it_behaves_like 'an idempotent resource' do 46 | let(:manifest) do 47 | <<-EOS 48 | class { 'puppet': 49 | server => true, 50 | server_version => '#{from_version_exact}', 51 | } 52 | EOS 53 | end 54 | end 55 | 56 | describe command('puppetserver --version') do 57 | its(:stdout) { is_expected.to match("puppetserver version: #{from_version}\n") } 58 | end 59 | 60 | describe service('puppetserver') do 61 | it { is_expected.to be_enabled } 62 | it { is_expected.to be_running } 63 | end 64 | 65 | describe port('8140') do 66 | it { is_expected.to be_listening } 67 | end 68 | end 69 | 70 | context "upgrade to #{to_version}" do 71 | it_behaves_like 'an idempotent resource' do 72 | let(:manifest) do 73 | <<-EOS 74 | class { 'puppet': 75 | server => true, 76 | server_version => '#{to_version_exact}', 77 | } 78 | EOS 79 | end 80 | end 81 | 82 | describe command('puppetserver --version') do 83 | its(:stdout) { is_expected.to match("puppetserver version: #{to_version}\n") } 84 | end 85 | 86 | describe service('puppetserver') do 87 | it { is_expected.to be_enabled } 88 | it { is_expected.to be_running } 89 | end 90 | 91 | describe port('8140') do 92 | it { is_expected.to be_listening } 93 | end 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /spec/defines/puppet_config_entry_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet::config::entry' do 4 | on_supported_os.each do |os, facts| 5 | context "on #{os}" do 6 | let(:facts) { facts } 7 | 8 | let(:title) { 'foo' } 9 | 10 | context 'with a plain value' do 11 | let :pre_condition do 12 | "class {'puppet': }" 13 | end 14 | let :params do 15 | { 16 | :key => 'foo', 17 | :value => 'bar', 18 | :section => 'main', 19 | :sectionorder => 1, 20 | } 21 | end 22 | it 'should contain the section header' do 23 | should contain_concat__fragment('puppet.conf_main').with_content("\n[main]") 24 | should contain_concat__fragment('puppet.conf_main').with_order("1_main ") 25 | end 26 | it 'should contain the section end' do 27 | should contain_concat__fragment('puppet.conf_main_end').with_content("\n") 28 | should contain_concat__fragment('puppet.conf_main_end').with_order("1_main~end") 29 | end 30 | it 'should contain the keyvalue pair' do 31 | should contain_concat__fragment('puppet.conf_main_foo').with_content(/^\s+foo = bar$/) 32 | should contain_concat__fragment('puppet.conf_main_foo').with_order("1_main_foo ") 33 | end 34 | end 35 | context 'with an array value' do 36 | let :pre_condition do 37 | "class {'puppet': }" 38 | end 39 | let :params do 40 | { 41 | :key => 'foo', 42 | :value => ['bar','baz'], 43 | :section => 'main', 44 | :sectionorder => 1, 45 | } 46 | end 47 | it 'should contain the section header' do 48 | should contain_concat__fragment('puppet.conf_main').with_content("\n[main]") 49 | should contain_concat__fragment('puppet.conf_main').with_order("1_main ") 50 | end 51 | it 'should contain the section end' do 52 | should contain_concat__fragment('puppet.conf_main_end').with_content("\n") 53 | should contain_concat__fragment('puppet.conf_main_end').with_order("1_main~end") 54 | end 55 | it 'should contain the keyvalue pair' do 56 | should contain_concat__fragment('puppet.conf_main_foo').with_content(/^\s+foo = bar,baz$/) 57 | should contain_concat__fragment('puppet.conf_main_foo').with_order("1_main_foo ") 58 | end 59 | end 60 | context 'with a custom joiner' do 61 | let :pre_condition do 62 | "class {'puppet': }" 63 | end 64 | let :params do 65 | { 66 | :key => 'foo', 67 | :value => ['bar','baz'], 68 | :joiner => ':', 69 | :section => 'main', 70 | :sectionorder => 1, 71 | } 72 | end 73 | it 'should contain the section header' do 74 | should contain_concat__fragment('puppet.conf_main').with_content("\n[main]") 75 | should contain_concat__fragment('puppet.conf_main').with_order("1_main ") 76 | end 77 | it 'should contain the section end' do 78 | should contain_concat__fragment('puppet.conf_main_end').with_content("\n") 79 | should contain_concat__fragment('puppet.conf_main_end').with_order("1_main~end") 80 | end 81 | it 'should contain the keyvalue pair' do 82 | should contain_concat__fragment('puppet.conf_main_foo').with_content(/^\s+foo = bar:baz$/) 83 | should contain_concat__fragment('puppet.conf_main_foo').with_order("1_main_foo ") 84 | end 85 | end 86 | 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /manifests/config.pp: -------------------------------------------------------------------------------- 1 | # Set up the puppet config 2 | # @api private 3 | class puppet::config ( 4 | # lint:ignore:parameter_types 5 | $allow_any_crl_auth = $puppet::allow_any_crl_auth, 6 | $auth_allowed = $puppet::auth_allowed, 7 | $ca_server = $puppet::ca_server, 8 | $ca_port = $puppet::ca_port, 9 | $certificate_revocation = $puppet::certificate_revocation, 10 | $dns_alt_names = $puppet::dns_alt_names, 11 | $module_repository = $puppet::module_repository, 12 | $pluginsource = $puppet::pluginsource, 13 | $pluginfactsource = $puppet::pluginfactsource, 14 | $puppet_dir = $puppet::dir, 15 | $agent_server_hostname = $puppet::agent_server_hostname, 16 | $syslogfacility = $puppet::syslogfacility, 17 | $srv_domain = $puppet::srv_domain, 18 | $use_srv_records = $puppet::use_srv_records, 19 | $additional_settings = $puppet::additional_settings, 20 | $client_certname = $puppet::client_certname, 21 | $hostprivkey = $puppet::hostprivkey, 22 | # lint:endignore 23 | ) { 24 | puppet::config::main { 25 | 'vardir': value => $puppet::vardir; 26 | 'logdir': value => $puppet::logdir; 27 | 'rundir': value => $puppet::rundir; 28 | 'ssldir': value => $puppet::ssldir; 29 | 'privatekeydir': value => '$ssldir/private_keys { group = service }'; 30 | 'hostprivkey': value => $hostprivkey; 31 | 'show_diff': value => $puppet::show_diff; 32 | 'codedir': value => $puppet::codedir; 33 | } 34 | 35 | if $module_repository and !empty($module_repository) { 36 | puppet::config::main { 'module_repository': value => $module_repository; } 37 | } 38 | if $ca_server and !empty($ca_server) { 39 | puppet::config::main { 'ca_server': value => $ca_server; } 40 | } 41 | if $ca_port { 42 | puppet::config::main { 'ca_port': value => $ca_port; } 43 | } 44 | if $certificate_revocation != undef { 45 | puppet::config::main { 'certificate_revocation': value => $certificate_revocation; } 46 | } 47 | if $dns_alt_names and !empty($dns_alt_names) { 48 | puppet::config::main { 'dns_alt_names': value => $dns_alt_names; } 49 | } 50 | if $use_srv_records { 51 | unless $srv_domain { 52 | fail('domain fact found to be undefined and $srv_domain is undefined') 53 | } 54 | puppet::config::main { 55 | 'use_srv_records': value => true; 56 | 'srv_domain': value => $srv_domain; 57 | } 58 | } else { 59 | puppet::config::main { 60 | 'server': value => pick($agent_server_hostname, $facts['networking']['fqdn']); 61 | } 62 | } 63 | if $pluginsource { 64 | puppet::config::main { 'pluginsource': value => $pluginsource; } 65 | } 66 | if $pluginfactsource { 67 | puppet::config::main { 'pluginfactsource': value => $pluginfactsource; } 68 | } 69 | if $syslogfacility and !empty($syslogfacility) { 70 | puppet::config::main { 'syslogfacility': value => $syslogfacility; } 71 | } 72 | if $client_certname { 73 | puppet::config::main { 74 | 'certname': value => $client_certname; 75 | } 76 | } 77 | 78 | $additional_settings.each |$key,$value| { 79 | puppet::config::main { $key: value => $value } 80 | } 81 | 82 | concat::fragment { 'puppet.conf_comment': 83 | target => "${puppet_dir}/puppet.conf", 84 | content => '# file managed by puppet', 85 | order => '0_comment', 86 | } 87 | 88 | file { $puppet_dir: 89 | ensure => directory, 90 | owner => $puppet::dir_owner, 91 | group => $puppet::dir_group, 92 | } 93 | -> case $facts['os']['family'] { 94 | 'Windows': { 95 | concat { "${puppet_dir}/puppet.conf": 96 | mode => $puppet::puppetconf_mode, 97 | } 98 | } 99 | 100 | default: { 101 | concat { "${puppet_dir}/puppet.conf": 102 | owner => 'root', 103 | group => $puppet::params::root_group, 104 | mode => $puppet::puppetconf_mode, 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /templates/server/post-receive.erb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Managed by Puppet 4 | # 5 | 6 | require 'fileutils' 7 | require 'etc' 8 | 9 | $stdout.sync = true 10 | $stderr.sync = true 11 | 12 | # Set this to where you want to keep your environments 13 | ENVIRONMENT_BASEDIR = "<%= scope.lookupvar("puppet::server::config::primary_envs_dir") %>" 14 | 15 | # post-receive hooks set GIT_DIR to the current repository. If you want to 16 | # clone from a non-local repository, set this to the URL of the repository, 17 | # such as git@git.host:puppet.git 18 | SOURCE_REPOSITORY = File.expand_path(ENV['GIT_DIR']) 19 | 20 | # Mapping of branches to directories. In many cases, the server branch is 21 | # checked out to the 'development' environment. 22 | BRANCH_MAP = { 23 | <% if @git_branch_map -%> 24 | <% @git_branch_map.sort.each do |g,p| -%> 25 | "<%= g %>" => "<%= p %>", 26 | <% end -%> 27 | <% end -%> 28 | } 29 | 30 | # The git_dir environment variable will override the --git-dir, so we remove it 31 | # to allow us to create new directories cleanly. 32 | ENV.delete('GIT_DIR') 33 | 34 | # Ensure that we have the underlying directories, otherwise the later commands 35 | # may fail in somewhat cryptic manners. 36 | unless File.directory? ENVIRONMENT_BASEDIR 37 | puts %Q{#{ENVIRONMENT_BASEDIR} does not exist, cannot create environment directories.} 38 | exit 1 39 | end 40 | 41 | # If we're running as root we change our UID to the owner of this file. 42 | file_uid = File.stat($0).uid 43 | if file_uid != Process.uid and Process.uid == 0 44 | Process::UID.change_privilege(file_uid) 45 | # Set LOGNAME and HOME directories to file-owning user's values 46 | # so git can read ~/.config/git/attributes (for example) without error 47 | file_pwuid = Etc::getpwuid(file_uid) 48 | ENV.store('LOGNAME',file_pwuid.name) 49 | ENV.store('HOME',file_pwuid.dir) 50 | end 51 | 52 | # Run a command, return its output and abort if it fails 53 | def do_cmd(cmd) 54 | ret = %x{#{cmd}} 55 | if $?.exitstatus != 0 56 | puts("'#{cmd}' failed. Giving up.") 57 | exit 1 58 | end 59 | ret 60 | end 61 | 62 | # You can push multiple refspecs at once, like 'git push origin branch1 branch2', 63 | # so we need to handle each one. 64 | $stdin.each_line do |line| 65 | oldrev, newrev, refname = line.split(" ") 66 | 67 | # Determine the branch name from the refspec we're received, which is in the 68 | # format refs/heads/, and make sure that it doesn't have any possibly 69 | # dangerous characters 70 | branchname = refname.sub(%r{^refs/heads/(.*$)}) { $1 } 71 | if branchname =~ /[\W]/ 72 | puts %Q{Branch "#{branchname}" contains non-word characters, ignoring it.} 73 | next 74 | end 75 | 76 | if BRANCH_MAP[branchname] != nil 77 | environment_name = BRANCH_MAP[branchname] 78 | environment_path = "#{ENVIRONMENT_BASEDIR}/#{BRANCH_MAP[branchname]}" 79 | else 80 | environment_name = branchname 81 | environment_path = "#{ENVIRONMENT_BASEDIR}/#{branchname}" 82 | end 83 | 84 | if newrev =~ /^0+$/ 85 | # We've received a push with a null revision, something like 000000000000, 86 | # which means that we should delete the given branch. 87 | puts "Deleting existing environment #{environment_name}" 88 | if File.directory? environment_path 89 | FileUtils.rm_rf environment_path, :secure => true 90 | end 91 | else 92 | # We have been given a branch that needs to be created or updated. If the 93 | # environment exists, update it. Else, create it. 94 | 95 | if File.directory? environment_path 96 | # Update an existing environment. We do a fetch and then reset in the 97 | # case that someone did a force push to a branch. 98 | 99 | puts "Updating existing environment #{environment_name}" 100 | Dir.chdir environment_path 101 | do_cmd("git fetch --all") 102 | do_cmd("git reset --hard 'origin/#{branchname}'") 103 | if File.exist? "#{environment_path}/.gitmodules" 104 | # ensure that we remove deleted sub modules too 105 | do_cmd("git status --short").split("\n").each do |file| 106 | # ?? old_submodule/ 107 | if file =~ /\s*\?{2}\s*(\S*)/ 108 | puts "Found a few unknown files.. deleting #{$1}" 109 | FileUtils.rm_rf $1, :secure => true 110 | end 111 | end 112 | do_cmd("git submodule sync") 113 | do_cmd("git submodule update --init --recursive") 114 | end 115 | else 116 | # Instantiate a new environment from the current repository. 117 | 118 | puts "Creating new environment #{environment_name}" 119 | do_cmd("git clone --recursive #{SOURCE_REPOSITORY} #{environment_path} --branch #{branchname}") 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /spec/classes/puppet_init_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet' do 4 | on_supported_os.each do |os, facts| 5 | context "on #{os}" do 6 | case facts[:os]['family'] 7 | when 'FreeBSD' 8 | puppet_major = facts[:puppetversion].to_i 9 | 10 | puppet_concat = '/usr/local/etc/puppet/puppet.conf' 11 | puppet_directory = '/usr/local/etc/puppet' 12 | puppet_package = "puppet#{puppet_major}" 13 | openvox_package = "openvox#{puppet_major}" 14 | puppetconf_mode = '0644' 15 | when 'windows' 16 | puppet_concat = 'C:/ProgramData/PuppetLabs/puppet/etc/puppet.conf' 17 | puppet_directory = 'C:/ProgramData/PuppetLabs/puppet/etc' 18 | puppet_package = 'puppet-agent' 19 | openvox_package = 'openvox-agent' 20 | puppetconf_mode = '0674' 21 | when 'Archlinux' 22 | puppet_concat = '/etc/puppetlabs/puppet/puppet.conf' 23 | puppet_directory = '/etc/puppetlabs/puppet' 24 | puppet_package = 'puppet' 25 | openvox_package = 'openvox' 26 | puppetconf_mode = '0644' 27 | else 28 | puppet_concat = '/etc/puppetlabs/puppet/puppet.conf' 29 | puppet_directory = '/etc/puppetlabs/puppet' 30 | puppet_package = 'puppet-agent' 31 | openvox_package = 'openvox-agent' 32 | puppetconf_mode = '0644' 33 | end 34 | 35 | let :facts do 36 | facts 37 | end 38 | 39 | describe 'with no custom parameters' do 40 | it { is_expected.to compile.with_all_deps unless facts[:os]['family'] == 'windows' } 41 | it { should contain_class('puppet::agent') } 42 | it { should contain_class('puppet::config') } 43 | it { should_not contain_class('puppet::server') } 44 | it { should contain_file(puppet_directory).with_ensure('directory') } 45 | it { should contain_concat(puppet_concat).with_mode(puppetconf_mode) } 46 | it { should contain_package(puppet_package) 47 | .with_ensure('present') 48 | .with_install_options(nil) 49 | } 50 | end 51 | 52 | describe 'with already installed packages' do 53 | describe 'legacy Perforce opensource packages' do 54 | let :facts do 55 | facts.merge(implementation: 'puppet') 56 | end 57 | it { should contain_package(puppet_package) 58 | .with_ensure('present') 59 | .with_install_options(nil) 60 | } 61 | end 62 | describe 'OpenVox packages' do 63 | let :facts do 64 | facts.merge(implementation: 'openvox') 65 | end 66 | it { should contain_package(openvox_package) 67 | .with_ensure('present') 68 | .with_install_options(nil) 69 | } 70 | end 71 | end 72 | 73 | describe 'with server => true', :unless => unsupported_puppetserver_osfamily(facts[:os]['family']) do 74 | let :params do { 75 | :server => true, 76 | } end 77 | 78 | it { is_expected.to compile.with_all_deps } 79 | it { should contain_class('puppet::server') } 80 | it { should contain_class('puppet::agent::service').that_requires('Class[puppet::server]') } 81 | end 82 | 83 | describe 'with empty ca_server' do 84 | let :params do { 85 | :ca_server => '', 86 | } end 87 | 88 | it { should_not contain_puppet__config__main('ca_server') } 89 | end 90 | 91 | describe 'with ca_server' do 92 | let :params do { 93 | :ca_server => 'ca.example.org', 94 | } end 95 | 96 | it { should contain_puppet__config__main('ca_server').with_value('ca.example.org') } 97 | end 98 | 99 | describe 'with undef ca_port' do 100 | let :params do { 101 | :ca_port => :undef, 102 | } end 103 | 104 | it { should_not contain_puppet__config__main('ca_port') } 105 | end 106 | 107 | describe 'with ca_port' do 108 | let :params do { 109 | :ca_port => 8140, 110 | } end 111 | 112 | it { should contain_puppet__config__main('ca_port').with_value(8140) } 113 | end 114 | 115 | describe 'with undef certificate_revocation' do 116 | let :params do { 117 | :certificate_revocation => :undef, 118 | } end 119 | 120 | it { should_not contain_puppet__config__main('certificate_revocation') } 121 | end 122 | 123 | describe 'with certificate_revocation' do 124 | let :params do { 125 | :certificate_revocation => 'leaf', 126 | } end 127 | 128 | it { should contain_puppet__config__main('certificate_revocation').with_value('leaf') } 129 | end 130 | 131 | describe 'with puppetconf_mode' do 132 | let :params do { 133 | :puppetconf_mode => '0640', 134 | } end 135 | 136 | it { should contain_concat(puppet_concat).with_mode('0640') } 137 | end 138 | 139 | # compilation is broken due to paths 140 | context 'on non-windows', unless: facts[:os]['family'] == 'windows' do 141 | describe 'with package_source => Httpurl' do 142 | let :params do { 143 | :package_source => 'https://example.com:123/test' 144 | } end 145 | 146 | it { is_expected.to compile } 147 | end 148 | 149 | describe 'with package_source => Unixpath' do 150 | let :params do { 151 | :package_source => '/test/folder/path/source.rpm' 152 | } end 153 | 154 | it { is_expected.to compile } 155 | end 156 | 157 | describe 'with package_source => Windowspath' do 158 | let :params do { 159 | :package_source => 'C:\test\folder\path\source.exe' 160 | } end 161 | 162 | it { is_expected.to compile } 163 | end 164 | 165 | describe 'with package_source => foo' do 166 | let :params do { 167 | :package_source => 'foo' 168 | } end 169 | 170 | it { is_expected.not_to compile } 171 | end 172 | end 173 | end 174 | end 175 | end 176 | -------------------------------------------------------------------------------- /templates/server/puppetserver/conf.d/puppetserver.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | # configuration for the JRuby interpreters 5 | jruby-puppet: { 6 | # Where the puppet-agent dependency places puppet, facter, etc... 7 | # Puppet server expects to load Puppet from this location 8 | ruby-load-path: [ 9 | <%- @server_ruby_load_paths.each do |ruby_load_path| -%> 10 | <%= ruby_load_path %>, 11 | <%- end -%> 12 | ] 13 | 14 | # This setting determines where JRuby will install gems. It is used for loading gems, 15 | # and also by the `puppetserver gem` command line tool. 16 | gem-home: <%= @server_jruby_gem_home %> 17 | 18 | # This setting defines the complete "GEM_PATH" for jruby. If set, it should include 19 | # the gem-home directory as well as any other directories that gems can be loaded 20 | # from (including the vendored gems directory for gems that ship with puppetserver) 21 | gem-path: [<%= @server_gem_paths.join(', ') %>] 22 | 23 | # PLEASE NOTE: Use caution when modifying the below settings. Modifying 24 | # these settings will change the value of the corresponding Puppet settings 25 | # for Puppet Server, but not for the Puppet CLI tools. This likely will not 26 | # be a problem with server-var-dir, server-run-dir, or server-log-dir unless 27 | # some critical setting in puppet.conf is interpolating the value of one 28 | # of the corresponding settings, but it is important that any changes made to 29 | # server-conf-dir and server-code-dir are also made to the corresponding Puppet 30 | # settings when running the Puppet CLI tools. See 31 | # https://docs.puppetlabs.com/puppetserver/latest/puppet_conf_setting_diffs.html#overriding-puppet-settings-in-puppet-server 32 | # for more information. 33 | 34 | # (optional) path to puppet conf dir; if not specified, will use 35 | # the puppet default 36 | server-conf-dir: <%= @server_dir %> 37 | 38 | # (optional) path to puppet code dir; if not specified, will use 39 | # the puppet default 40 | server-code-dir: <%= @codedir %> 41 | 42 | # (optional) path to puppet var dir; if not specified, will use 43 | # the puppet default 44 | server-var-dir: <%= @server_puppetserver_vardir %> 45 | 46 | # (optional) path to puppet run dir; if not specified, will use 47 | # the puppet default 48 | server-run-dir: <%= @server_puppetserver_rundir %> 49 | 50 | # (optional) path to puppet log dir; if not specified, will use 51 | # the puppet default 52 | server-log-dir: <%= @server_puppetserver_logdir %> 53 | 54 | # (optional) maximum number of JRuby instances to allow 55 | max-active-instances: <%= @server_max_active_instances %> 56 | 57 | # (optional) the number of HTTP requests a given JRuby instance will handle in its lifetime. 58 | max-requests-per-instance: <%= @server_max_requests_per_instance %> 59 | 60 | # (optional) The maximum number of requests that may be queued waiting to borrow a JRuby from the pool. 61 | max-queued-requests: <%= @server_max_queued_requests %> 62 | 63 | # (optional) Sets the upper limit for the random sleep set as a Retry-After header on 503 responses returned when max-queued-requests is enabled. 64 | max-retry-delay: <%= @server_max_retry_delay %> 65 | 66 | # (optional) enable or disable environment class cache 67 | environment-class-cache-enabled: <%= @server_environment_class_cache_enabled %> 68 | 69 | # (optional) A map of environment variables which are made visible to 70 | # Ruby code running within JRuby, for example, via the Ruby ENV class. 71 | # By default, the only environment variables whose values are set into JRuby from the shell are HOME and PATH. 72 | # The default value for the GEM_HOME environment variable in JRuby is set from the value provided for the 73 | # jruby-puppet.gem-home key. 74 | # Any variable set from the map for the environment-vars key overrides these defaults. 75 | # Avoid overriding HOME, PATH, or GEM_HOME here because these values are already configurable via the shell 76 | # or jruby-puppet.gem-home. 77 | environment-vars: { 78 | <%- @server_environment_vars.each do |env_key, env_val| -%> 79 | "<%= env_key %>" : <%= env_val %> 80 | <%- end -%> 81 | } 82 | <%- if @compile_mode %> 83 | 84 | compile-mode: <%= @compile_mode %> 85 | <%- end -%> 86 | multithreaded: <%= @server_multithreaded %> 87 | } 88 | <%- if @versioned_code_id and @versioned_code_content -%> 89 | 90 | versioned-code: { 91 | code-id-command: <%= @versioned_code_id %> 92 | code-content-command: <%= @versioned_code_content %> 93 | } 94 | <%- end -%> 95 | 96 | # settings related to HTTPS client requests made by Puppet Server 97 | http-client: { 98 | # A list of acceptable protocols for making HTTPS requests 99 | ssl-protocols: [ 100 | <%- @server_ssl_protocols.each do |protocol| -%> 101 | <%= protocol %>, 102 | <%- end -%> 103 | ] 104 | 105 | # A list of acceptable cipher suites for making HTTPS requests 106 | cipher-suites: [ 107 | <%- @server_cipher_suites.each do |cipher| -%> 108 | <%= cipher %>, 109 | <%- end -%> 110 | ] 111 | 112 | # Whether to enable http-client metrics; defaults to 'true'. 113 | metrics-enabled: <%= @server_metrics %> 114 | 115 | # The amount of time, in milliseconds, that an outbound HTTP connection 116 | # will wait for data to be available before closing the socket. If not 117 | # defined, defaults to 20 minutes. If 0, the timeout is infinite and if 118 | # negative, the value is undefined by the application and governed by the 119 | # system default behavior. 120 | idle-timeout-milliseconds: <%= @server_idle_timeout %> 121 | 122 | # The amount of time, in milliseconds, that an outbound HTTP connection will 123 | # wait to connect before giving up. Defaults to 2 minutes if not set. If 0, 124 | # the timeout is infinite and if negative, the value is undefined in the 125 | # application and governed by the system default behavior. 126 | connect-timeout-milliseconds: <%= @server_connect_timeout %> 127 | } 128 | 129 | # settings related to profiling the puppet Ruby code 130 | profiler: { 131 | # enable or disable profiling for the Ruby code; 132 | enabled: <%= @server_profiler %> 133 | } 134 | 135 | # Settings related to telemetry 136 | dropsonde: { 137 | # enable or disable telemetry 138 | enabled: <%= @server_telemetry %> 139 | } 140 | -------------------------------------------------------------------------------- /spec/classes/puppet_config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet' do 4 | on_supported_os.each do |os, os_facts| 5 | context "on #{os}" do 6 | case os_facts[:os]['family'] 7 | when 'FreeBSD' 8 | dir_owner = 'root' 9 | dir_group = nil 10 | confdir = '/usr/local/etc/puppet' 11 | logdir = '/var/log/puppet' 12 | rundir = '/var/run/puppet' 13 | ssldir = '/var/puppet/ssl' 14 | vardir = '/var/puppet' 15 | when 'windows' 16 | dir_owner = nil 17 | dir_group = nil 18 | confdir = 'C:/ProgramData/PuppetLabs/puppet/etc' 19 | logdir = 'C:/ProgramData/PuppetLabs/puppet/var/log' 20 | rundir = 'C:/ProgramData/PuppetLabs/puppet/var/run' 21 | ssldir = 'C:/ProgramData/PuppetLabs/puppet/etc/ssl' 22 | vardir = 'C:/ProgramData/PuppetLabs/puppet/var' 23 | when 'Archlinux' 24 | dir_owner = 'root' 25 | dir_group = nil 26 | confdir = '/etc/puppetlabs/puppet' 27 | logdir = '/var/log/puppetlabs/puppet' 28 | rundir = '/var/run/puppetlabs' 29 | ssldir = '/etc/puppetlabs/puppet/ssl' 30 | vardir = '/opt/puppetlabs/puppet/cache' 31 | else 32 | dir_owner = 'root' 33 | dir_group = nil 34 | confdir = '/etc/puppetlabs/puppet' 35 | logdir = '/var/log/puppetlabs/puppet' 36 | rundir = '/var/run/puppetlabs' 37 | ssldir = '/etc/puppetlabs/puppet/ssl' 38 | vardir = '/opt/puppetlabs/puppet/cache' 39 | end 40 | 41 | let :facts do 42 | override_facts(os_facts, networking: {domain: 'example.org'}) 43 | end 44 | 45 | let :params do 46 | {} 47 | end 48 | 49 | describe 'with default parameters' do 50 | it { is_expected.to contain_file(confdir).with_owner(dir_owner).with_group(dir_group) } 51 | it { is_expected.not_to contain_puppet__config__main('default_manifest') } 52 | it { is_expected.not_to contain_file('/etc/puppet/manifests/default_manifest.pp') } 53 | it { is_expected.not_to contain_puppet__config__main('reports') } 54 | it { is_expected.to contain_puppet__config__main('vardir').with_value(vardir) } 55 | it { is_expected.to contain_puppet__config__main('logdir').with_value(logdir) } 56 | it { is_expected.to contain_puppet__config__main('rundir').with_value(rundir) } 57 | it { is_expected.to contain_puppet__config__main('ssldir').with_value(ssldir) } 58 | it { is_expected.to contain_puppet__config__main('privatekeydir').with_value('$ssldir/private_keys { group = service }') } 59 | it { is_expected.to contain_puppet__config__main('hostprivkey').with_value('$privatekeydir/$certname.pem { mode = 640 }') } 60 | it { is_expected.to contain_puppet__config__main('show_diff').with_value('false') } 61 | it { is_expected.to contain_puppet__config__main('server').with_value(facts[:networking]['fqdn']) } 62 | end 63 | 64 | describe "when dns_alt_names => ['foo','bar']" do 65 | let :params do 66 | super().merge(dns_alt_names: %w[foo bar]) 67 | end 68 | 69 | it { is_expected.to contain_puppet__config__main('dns_alt_names').with_value(%w[foo bar]) } 70 | end 71 | 72 | describe "when syslogfacility => 'local6'" do 73 | let :params do 74 | super().merge(syslogfacility: 'local6') 75 | end 76 | 77 | it { is_expected.to contain_puppet__config__main('syslogfacility').with_value('local6') } 78 | end 79 | 80 | describe "when module_repository => 'https://myforgeapi.example.com'" do 81 | let :params do 82 | super().merge(module_repository: 'https://myforgeapi.example.com') 83 | end 84 | 85 | it { is_expected.to contain_puppet__config__main('module_repository').with_value('https://myforgeapi.example.com') } 86 | end 87 | 88 | describe 'when use_srv_records => true' do 89 | let :params do 90 | super().merge(use_srv_records: true) 91 | end 92 | 93 | context 'domain fact is defined' do 94 | it { is_expected.to contain_puppet__config__main('use_srv_records').with_value('true') } 95 | it { is_expected.to contain_puppet__config__main('srv_domain').with_value('example.org') } 96 | it { is_expected.to contain_puppet__config__main('pluginsource').with_value('puppet:///plugins') } 97 | it { is_expected.to contain_puppet__config__main('pluginfactsource').with_value('puppet:///pluginfacts') } 98 | it { is_expected.not_to contain_puppet__config__main('server') } 99 | end 100 | 101 | context 'domain fact is unset' do 102 | let(:facts) { override_facts(super(), networking: {domain: nil}) } 103 | 104 | it { is_expected.to raise_error(Puppet::Error, /domain fact found to be undefined and \$srv_domain is undefined/) } 105 | end 106 | 107 | context 'is overriden via param' do 108 | let :params do 109 | super().merge(srv_domain: 'special.example.com') 110 | end 111 | 112 | it { is_expected.to contain_puppet__config__main('use_srv_records').with_value(true) } 113 | it { is_expected.to contain_puppet__config__main('srv_domain').with_value('special.example.com') } 114 | end 115 | end 116 | 117 | describe 'client_certname' do 118 | let(:node) { 'client.example.com' } 119 | 120 | context 'with client_certname => trusted certname' do 121 | it { is_expected.to contain_puppet__config__main('certname').with_value('client.example.com') } 122 | end 123 | 124 | context 'with client_certname => "foobar"' do 125 | let :params do 126 | super().merge(client_certname: 'foobar') 127 | end 128 | 129 | it { is_expected.to contain_puppet__config__main('certname').with_value('foobar') } 130 | end 131 | 132 | context 'with client_certname => false' do 133 | let :params do 134 | super().merge(client_certname: false) 135 | end 136 | 137 | it { is_expected.not_to contain_puppet__config__main('certname') } 138 | end 139 | end 140 | 141 | context 'agent_server_hostname' do 142 | describe "when agent_server_hostname => 'myserver.example.com'" do 143 | let :params do 144 | super().merge(agent_server_hostname: 'myserver.example.com') 145 | end 146 | 147 | it { is_expected.to contain_puppet__config__main('server').with_value('myserver.example.com') } 148 | end 149 | 150 | # puppetmaster is provided via the Foreman ENC as a global variable 151 | context 'with global puppetmaster' do 152 | let(:facts) { super().merge(puppetmaster: 'global.example.com') } 153 | 154 | describe 'it overrides fqdn' do 155 | it { is_expected.to contain_puppet__config__main('server').with_value('global.example.com') } 156 | end 157 | 158 | describe 'the agent_server_hostname parameter overrides global puppetmaster' do 159 | let(:params) { super().merge(agent_server_hostname: 'myserver.example.com') } 160 | 161 | it { is_expected.to contain_puppet__config__main('server').with_value('myserver.example.com') } 162 | end 163 | end 164 | end 165 | 166 | describe 'with custom hostprivkey set' do 167 | let :params do 168 | super().merge(hostprivkey: '$privatekeydir/$certname.pem { mode = 660 }') 169 | end 170 | 171 | it { is_expected.to contain_puppet__config__main('hostprivkey').with_value('$privatekeydir/$certname.pem { mode = 660 }') } 172 | end 173 | 174 | describe 'with additional settings' do 175 | let :params do 176 | super().merge(additional_settings: { disable_warnings: 'deprecations' }) 177 | end 178 | 179 | it { is_expected.to contain_puppet__config__main('disable_warnings').with_value('deprecations') } 180 | end 181 | end 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Puppet Forge](https://img.shields.io/puppetforge/v/theforeman/puppet.svg)](https://forge.puppetlabs.com/theforeman/puppet) 2 | [![CI](https://github.com/theforeman/puppet-puppet/actions/workflows/ci.yml/badge.svg?event=schedule)](https://github.com/theforeman/puppet-puppet/actions/workflows/ci.yml) 3 | 4 | # Puppet module for installing the Puppet agent and server 5 | 6 | Installs and configures the Puppet agent and optionally a Puppet server (when 7 | `server` is true). Part of the [Foreman installer](https://github.com/theforeman/foreman-installer) 8 | or to be used as a Puppet module. 9 | 10 | Many puppet.conf options for agents, servers and other are parameterized, with 11 | class documentation provided at the top of the manifests. In addition, there 12 | are hash parameters for each configuration section that can be used to supply 13 | any options that are not explicitly supported. 14 | 15 | ## Compatibility 16 | 17 | See the module metadata for supported operating systems and compatible Puppet 18 | versions. The Puppetserver version should also match this. 19 | 20 | ## Environments support 21 | 22 | The module helps configure Puppet environments using directory environments. 23 | These are set up under /etc/puppetlabs/code/environments. 24 | 25 | ## Git repo support 26 | 27 | Environments can be backed by git by setting `server_git_repo` to true, which 28 | sets up `/var/lib/puppet/puppet.git` where each branch maps to one environment. 29 | Avoid using 'server' as this name isn't permitted. On each push to the repo, a 30 | hook updates `/etc/puppet/environments` with the contents of the branch. 31 | 32 | Permissions can be controlled via the `git_repo_{user,group,hook_mode,umask}` 33 | parameters. 34 | 35 | Requires [puppetlabs/vcsrepo](https://forge.puppetlabs.com/puppetlabs/vcsrepo) >= 5.2.0. 36 | 37 | ## Foreman integration 38 | 39 | The Foreman integration is optional, but on by default when `server` is true. 40 | It can be disabled by setting `server_foreman` to false. 41 | 42 | Requires [theforeman/puppetserver_foreman](https://forge.puppetlabs.com/theforeman/puppetserver_foreman). 43 | 44 | Since version 15.0.0 the integration bits depend on the standalone module where 45 | previously it depended on 46 | [theforeman/foreman](https://forge.puppetlabs.com/theforeman/foreman) 47 | 48 | There is also optional integration for [katello/certs](https://forge.puppetlabs.com/katello/certs). 49 | This can be enabled via Hiera: 50 | 51 | ```yaml 52 | puppet::server::foreman::katello: true 53 | ``` 54 | 55 | Then the `foreman_ssl_{ca,cert,key}` parameters are ignored and `certs::puppet` is used as a source. 56 | 57 | ## PuppetDB integration 58 | 59 | The Puppet server can be configured to export catalogs and reports to a PuppetDB instance, using the puppetlabs/puppetdb module. 60 | Use its `puppetdb::server` class to install the PuppetDB server and this module to configure the Puppet server to connect to PuppetDB. 61 | 62 | Requires [puppetlabs/puppetdb](https://forge.puppetlabs.com/puppetlabs/puppetdb) 63 | 64 | ```puppet 65 | class { 'puppet': 66 | server => true, 67 | server_reports => 'puppetdb,foreman', 68 | server_storeconfigs => true, 69 | } 70 | class { 'puppet::server::puppetdb': 71 | server => 'mypuppetdb.example.com', 72 | } 73 | ``` 74 | 75 | Above example manages Puppetserver + PuppetDB integration. 76 | It won't install the PuppetDB. 77 | To do so, you also need the `puppetdb` class 78 | 79 | ```puppet 80 | class { 'puppet': 81 | server => true, 82 | server_reports => 'puppetdb,foreman', 83 | server_storeconfigs => true, 84 | } 85 | include puppetdb 86 | class { 'puppet::server::puppetdb': 87 | server => 'mypuppetdb.example.com', 88 | } 89 | ``` 90 | 91 | Then the PuppetDB module will also configure postgresql and setup the database. 92 | If you want to manage postgresql installation on your own: 93 | 94 | ```puppet 95 | class { 'postgresql::globals': 96 | encoding => 'UTF-8', 97 | locale => 'en_US.UTF-8', 98 | version => '15', 99 | manage_package_repo => true, 100 | } 101 | class { 'postgresql::server': 102 | listen_addresses => '127.0.0.1', 103 | } 104 | postgresql::server::extension { 'pg_trgm': 105 | database => 'puppetdb', 106 | require => Postgresql::Server::Db['puppetdb'], 107 | before => Service['puppetdb'], 108 | } 109 | class { 'puppetdb': 110 | manage_dbserver => false, 111 | } 112 | class { 'puppet::server::puppetdb': 113 | server => 'mypuppetdb.example.com', 114 | } 115 | ``` 116 | 117 | Above code will install Puppetserver/PuppetDB/PostgreSQL on a single server. 118 | It will use the upstream postgresql repositories. 119 | It was tested on Ubuntu. 120 | 121 | Please also make sure your puppetdb ciphers are compatible with your puppet server ciphers, ie that the two following parameters match: 122 | 123 | ``` 124 | puppet::server::cipher_suites 125 | puppetdb::server::cipher_suites 126 | ``` 127 | 128 | By default, the Perforce packages are used. 129 | [Since November 2024, they don't receive updates anymore](https://www.puppet.com/blog/open-source-puppet-updates-2025). 130 | To use the new [OpenVoxProject packages](https://voxpupuli.org/openvox/), update the package names: 131 | 132 | ```yaml 133 | --- 134 | puppet::client_package: openvox-agent 135 | puppet::server_package: openvox-server 136 | puppetdb::puppetdb_package: openvoxdb 137 | puppetdb::master::config::terminus_package: openvoxdb-termini 138 | ``` 139 | 140 | If you replace the Perforce agent or server packages and switch to the OpenVox implementation by hand, without setting anything in Hiera, the module will detect this and just keeps working, no changes required. 141 | 142 | Further installation instructions are also documented on the [OpenVox project page](https://voxpupuli.org/openvox/install/). 143 | 144 | # Installation 145 | 146 | Available from GitHub (via cloning or tarball), [Puppet Forge](https://forge.puppetlabs.com/theforeman/puppet) 147 | or as part of the Foreman installer. 148 | 149 | # Usage 150 | 151 | As a parameterized class, all the configurable options can be overridden from your 152 | wrapper classes or even your ENC (if it supports param classes). For example: 153 | 154 | ```puppet 155 | # Agent and cron (or daemon): 156 | class { 'puppet': runmode => 'cron', agent_server_hostname => 'hostname' } 157 | 158 | # Agent and puppetserver: 159 | class { 'puppet': server => true } 160 | 161 | # You want to use git? 162 | class { 'puppet': 163 | server => true 164 | server_git_repo => true 165 | } 166 | 167 | # Maybe you're using gitolite, new hooks, and a different port? 168 | class { 'puppet': 169 | server => true 170 | server_port => 8141, 171 | server_git_repo => true, 172 | server_git_repo_path => '/var/lib/gitolite/repositories/puppet.git', 173 | server_post_hook_name => 'post-receive.puppet', 174 | server_post_hook_content => 'puppetserver/post-hook.puppet', 175 | } 176 | 177 | # Configure server without Foreman integration 178 | class { 'puppet': 179 | server => true, 180 | server_foreman => false, 181 | server_reports => 'store', 182 | server_external_nodes => '', 183 | } 184 | 185 | # Want to integrate with an existing PuppetDB? 186 | class { 'puppet': 187 | server => true, 188 | server_reports => 'puppetdb,foreman', 189 | server_storeconfigs => true, 190 | } 191 | class { 'puppet::server::puppetdb': 192 | server => 'mypuppetdb.example.com', 193 | } 194 | ``` 195 | 196 | Look in _init.pp_ for what can be configured this way, see Contributing if anything 197 | doesn't work. 198 | 199 | To use this in standalone mode, edit a file (e.g. install.pp), put in a class resource, 200 | as per the examples above, and the execute _puppet apply_ e.g: 201 | 202 | ```sh 203 | puppet apply --modulepath /path_to/extracted_tarball < true } 205 | EOF 206 | ``` 207 | 208 | # Advanced scenarios 209 | 210 | An HTTP (non-SSL) puppetserver instance can be set up (standalone or in addition to 211 | the SSL instance) by setting the `server_http` parameter to `true`. This is useful for 212 | reverse proxy or load balancer scenarios where the proxy/load balancer takes care of SSL 213 | termination. The HTTP puppetserver instance expects the `X-Client-Verify`, `X-SSL-Client-DN` 214 | and `X-SSL-Subject` HTTP headers to have been set on the front end server. 215 | 216 | The listening port can be configured by setting `server_http_port` (which defaults to 8139). 217 | 218 | For puppetserver, this HTTP instance accepts **ALL** connections and no further restrictions can be configured. 219 | 220 | **Note that running an HTTP puppetserver is a huge security risk when improperly 221 | configured. Allowed hosts should be tightly controlled; anyone with access to an allowed 222 | host can access all client catalogues and client certificates.** 223 | 224 | ```puppet 225 | # Configure an HTTP puppetserver vhost in addition to the standard SSL vhost 226 | class { '::puppet': 227 | server => true, 228 | server_http => true, 229 | server_http_port => 8130, # default: 8139 230 | } 231 | ``` 232 | 233 | # Contributing 234 | 235 | * Fork the project 236 | * Commit and push until you are happy with your contribution 237 | 238 | # More info 239 | 240 | See https://theforeman.org or at #theforeman irc channel on freenode 241 | 242 | Copyright (c) 2010-2012 Ohad Levy 243 | 244 | This program and entire repository is free software: you can redistribute it and/or modify 245 | it under the terms of the GNU General Public License as published by 246 | the Free Software Foundation, either version 3 of the License, or 247 | any later version. 248 | 249 | This program is distributed in the hope that it will be useful, 250 | but WITHOUT ANY WARRANTY; without even the implied warranty of 251 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 252 | GNU General Public License for more details. 253 | You should have received a copy of the GNU General Public License 254 | along with this program. If not, see . 255 | -------------------------------------------------------------------------------- /manifests/server/config.pp: -------------------------------------------------------------------------------- 1 | # Set up the puppet server config 2 | # @api private 3 | class puppet::server::config inherits puppet::config { 4 | contain 'puppet::server::puppetserver' 5 | unless empty($puppet::server::puppetserver_vardir) { 6 | puppet::config::server { 7 | 'vardir': value => $puppet::server::puppetserver_vardir; 8 | } 9 | } 10 | unless empty($puppet::server::puppetserver_rundir) { 11 | puppet::config::server { 12 | 'rundir': value => $puppet::server::puppetserver_rundir; 13 | } 14 | } 15 | unless empty($puppet::server::puppetserver_logdir) { 16 | puppet::config::server { 17 | 'logdir': value => $puppet::server::puppetserver_logdir; 18 | } 19 | } 20 | 21 | # Mirror the relationship, as defined() is parse-order dependent 22 | # Ensures puppetservers certs are generated before the proxy is needed 23 | if defined(Class['foreman_proxy::config']) and $foreman_proxy::ssl { 24 | Class['puppet::server::config'] ~> Class['foreman_proxy::config'] 25 | Class['puppet::server::config'] ~> Class['foreman_proxy::service'] 26 | } 27 | 28 | # And before Foreman's cert-using service needs it 29 | if defined(Class['foreman::service']) and $foreman::ssl { 30 | Class['puppet::server::config'] -> Class['foreman::service'] 31 | } 32 | 33 | ## General configuration 34 | $ca_server = $puppet::ca_server 35 | $ca_port = $puppet::ca_port 36 | $server_external_nodes = $puppet::server::external_nodes 37 | $server_environment_timeout = $puppet::server::environment_timeout 38 | $trusted_external_command = $puppet::server::trusted_external_command 39 | $primary_envs_dir = $puppet::server::envs_dir[0] 40 | 41 | if $server_external_nodes and $server_external_nodes != '' { 42 | class { 'puppet::server::enc': 43 | enc_path => $server_external_nodes, 44 | } 45 | } 46 | 47 | if $trusted_external_command { 48 | puppet::config::server { 49 | 'trusted_external_command': value => $trusted_external_command, 50 | } 51 | } 52 | 53 | $autosign = ($puppet::server::autosign =~ Boolean)? { 54 | true => $puppet::server::autosign, 55 | false => "${puppet::server::autosign} { mode = ${puppet::server::autosign_mode} }" 56 | } 57 | 58 | puppet::config::main { 59 | 'reports': value => $puppet::server::reports; 60 | 'environmentpath': value => $puppet::server::envs_dir.join(':'); 61 | } 62 | if $puppet::server::hiera_config and !empty($puppet::server::hiera_config) { 63 | puppet::config::main { 64 | 'hiera_config': value => $puppet::server::hiera_config; 65 | } 66 | } 67 | if $puppet::server::common_modules_path and !empty($puppet::server::common_modules_path) { 68 | puppet::config::main { 69 | 'basemodulepath': value => $puppet::server::common_modules_path, joiner => ':'; 70 | } 71 | } 72 | if $puppet::server::default_manifest { 73 | puppet::config::main { 74 | 'default_manifest': value => $puppet::server::default_manifest_path; 75 | } 76 | } 77 | 78 | puppet::config::server { 79 | 'autosign': value => $autosign; 80 | 'ca': value => $puppet::server::ca; 81 | 'certname': value => $puppet::server::certname; 82 | 'parser': value => $puppet::server::parser; 83 | 'strict_variables': value => $puppet::server::strict_variables; 84 | 'storeconfigs': value => $puppet::server::storeconfigs; 85 | } 86 | 87 | if $puppet::server::ssl_dir_manage { 88 | puppet::config::server { 89 | 'ssldir': value => $puppet::server::ssl_dir; 90 | } 91 | } 92 | if $server_environment_timeout { 93 | puppet::config::server { 94 | 'environment_timeout': value => $server_environment_timeout; 95 | } 96 | } 97 | 98 | $puppet::server::additional_settings.each |$key,$value| { 99 | puppet::config::server { $key: value => $value } 100 | } 101 | 102 | file { "${puppet::vardir}/reports": 103 | ensure => directory, 104 | owner => $puppet::server::user, 105 | group => $puppet::server::group, 106 | mode => '0750', 107 | } 108 | 109 | if '/usr/share/puppet/modules' in $puppet::server::common_modules_path { 110 | # Create Foreman share dir which does not depend on Puppet version 111 | exec { 'mkdir -p /usr/share/puppet/modules': 112 | creates => '/usr/share/puppet/modules', 113 | path => ['/usr/bin', '/bin'], 114 | } 115 | } 116 | 117 | ## SSL and CA configuration 118 | # Open read permissions to private keys to puppet group for foreman, proxy etc. 119 | file { "${puppet::server::ssl_dir}/private_keys": 120 | ensure => directory, 121 | owner => $puppet::server::user, 122 | group => $puppet::server::group, 123 | mode => '0750', 124 | require => Exec['puppet_server_config-create_ssl_dir'], 125 | } 126 | 127 | if $puppet::server::ssl_key_manage { 128 | file { "${puppet::server::ssl_dir}/private_keys/${puppet::server::certname}.pem": 129 | owner => $puppet::server::user, 130 | group => $puppet::server::group, 131 | mode => '0640', 132 | } 133 | } 134 | 135 | if $puppet::server::custom_trusted_oid_mapping { 136 | $_custom_trusted_oid_mapping = { 137 | oid_mapping => $puppet::server::custom_trusted_oid_mapping, 138 | } 139 | file { "${puppet::dir}/custom_trusted_oid_mapping.yaml": 140 | ensure => file, 141 | owner => 'root', 142 | group => $puppet::params::root_group, 143 | mode => '0644', 144 | content => to_yaml($_custom_trusted_oid_mapping), 145 | } 146 | } 147 | 148 | # If the ssl dir is not the default dir, it needs to be created before running 149 | # the generate ca cert or it will fail. 150 | exec { 'puppet_server_config-create_ssl_dir': 151 | creates => $puppet::server::ssl_dir, 152 | command => "/bin/mkdir -p ${puppet::server::ssl_dir}", 153 | umask => '0022', 154 | } 155 | 156 | # Generate a new CA and host cert if our host cert doesn't exist 157 | if $puppet::server::ca { 158 | exec { 'puppet_server_config-generate_ca_cert': 159 | creates => $puppet::server::ssl_ca_cert, 160 | command => "${puppet::puppetserver_cmd} ca setup", 161 | umask => '0022', 162 | require => [ 163 | Concat["${puppet::server::dir}/puppet.conf"], 164 | Exec['puppet_server_config-create_ssl_dir'], 165 | ], 166 | } 167 | } elsif $puppet::server::ca_crl_sync { 168 | # If not a ca AND sync the crl from the ca server 169 | if $server_facts['servername'] { 170 | file { $puppet::server::ssl_ca_crl: 171 | ensure => file, 172 | owner => $puppet::server::user, 173 | group => $puppet::server::group, 174 | mode => '0644', 175 | content => file($settings::cacrl, $settings::hostcrl, '/dev/null'), 176 | } 177 | } 178 | } 179 | 180 | # autosign file 181 | if $puppet::server_ca and !($puppet::server::autosign =~ Boolean) { 182 | if $puppet::server::autosign_content or $puppet::server::autosign_source { 183 | if !empty($puppet::server::autosign_entries) { 184 | fail('Cannot set both autosign_content/autosign_source and autosign_entries') 185 | } 186 | $autosign_content = $puppet::server::autosign_content 187 | } elsif !empty($puppet::server::autosign_entries) { 188 | $autosign_content = template('puppet/server/autosign.conf.erb') 189 | } else { 190 | $autosign_content = undef 191 | } 192 | file { $puppet::server::autosign: 193 | ensure => file, 194 | owner => $puppet::server::user, 195 | group => $puppet::server::group, 196 | mode => $puppet::server::autosign_mode, 197 | content => $autosign_content, 198 | source => $puppet::server::autosign_source, 199 | } 200 | } 201 | 202 | # only manage this file if we provide content 203 | if $puppet::server::default_manifest and $puppet::server::default_manifest_content != '' { 204 | file { $puppet::server::default_manifest_path: 205 | ensure => file, 206 | owner => $puppet::user, 207 | group => $puppet::group, 208 | mode => '0644', 209 | content => $puppet::server::default_manifest_content, 210 | } 211 | } 212 | 213 | ## Environments 214 | # location where our puppet environments are located 215 | if $puppet::server::envs_target and $puppet::server::envs_target != '' { 216 | $ensure = 'link' 217 | } else { 218 | $ensure = 'directory' 219 | } 220 | 221 | file { $puppet::server::envs_dir: 222 | ensure => $ensure, 223 | owner => $puppet::server::environments_owner, 224 | group => $puppet::server::environments_group, 225 | mode => $puppet::server::environments_mode, 226 | target => $puppet::server::envs_target, 227 | recurse => $puppet::server::environments_recurse, 228 | force => true, 229 | } 230 | 231 | if $puppet::server::git_repo { 232 | vcsrepo { 'puppet_repo': 233 | ensure => 'bare', 234 | provider => 'git', 235 | path => $puppet::server::git_repo_path, 236 | user => $puppet::server::git_repo_user, 237 | group => $puppet::server::git_repo_group, 238 | umask => $puppet::server::git_repo_umask, 239 | require => File[$primary_envs_dir], 240 | } 241 | 242 | $git_branch_map = $puppet::server::git_branch_map 243 | # git post hook to auto generate an environment per branch 244 | file { "${puppet::server::git_repo_path}/hooks/${puppet::server::post_hook_name}": 245 | content => template($puppet::server::post_hook_content), 246 | owner => $puppet::server::git_repo_user, 247 | group => $puppet::server::git_repo_group, 248 | mode => $puppet::server::git_repo_hook_mode, 249 | require => Vcsrepo['puppet_repo'], 250 | } 251 | } 252 | 253 | file { $puppet::sharedir: 254 | ensure => directory, 255 | } 256 | 257 | if $puppet::server::common_modules_path and !empty($puppet::server::common_modules_path) { 258 | file { $puppet::server::common_modules_path: 259 | ensure => directory, 260 | owner => $puppet::server_environments_owner, 261 | group => $puppet::server_environments_group, 262 | mode => $puppet::server_environments_mode, 263 | } 264 | } 265 | 266 | if $puppet::server::foreman { 267 | contain puppet::server::foreman 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Checklist (and a short version for the impatient) 2 | ================================================= 3 | 4 | * Commits: 5 | 6 | - Make commits of logical units. 7 | 8 | - Check for unnecessary whitespace with "git diff --check" before 9 | committing. 10 | 11 | - Commit using Unix line endings (check the settings around "crlf" in 12 | git-config(1)). 13 | 14 | - Do not check in commented out code or unneeded files. 15 | 16 | - The first line of the commit message should be a short 17 | description (50 characters is the soft limit, excluding ticket 18 | number(s)), and should skip the full stop. 19 | 20 | - If you have a [https://projects.theforeman.org/projects/puppet-foreman/issues](Redmine issue) 21 | number, associate the issue in the message. The first line should start 22 | with the issue number in the form "fixes #XXXX - rest of message". 23 | [More information on the Redmine style](https://projects.theforeman.org/projects/foreman/wiki/Reviewing_patches-commit_message_format). 24 | Tickets are not required for our installer Puppet modules. 25 | 26 | - If you have a GitHub issue number, associate the issue in the message. 27 | End the commit message with "Fixes GH-1234" on a new line. 28 | 29 | - The body should provide a meaningful commit message, which: 30 | 31 | - uses the imperative, present tense: "change", not "changed" or 32 | "changes". 33 | 34 | - includes motivation for the change, and contrasts its 35 | implementation with the previous behavior. 36 | 37 | - Make sure that you have tests for the bug you are fixing, or 38 | feature you are adding. 39 | 40 | - Make sure the test suites passes after your commit: 41 | `bundle exec rake spec` More information on [testing](#Testing) below 42 | 43 | - When introducing a large change, make sure it is properly 44 | documented in the README.md 45 | 46 | * Submission: 47 | 48 | * Pre-requisites: 49 | 50 | - Make sure you have a [GitHub account](https://github.com/join) 51 | 52 | * Preferred method: 53 | 54 | - Fork the repository on GitHub. 55 | 56 | - Push your changes to a topic branch in your fork of the 57 | repository, in a new branch. 58 | 59 | - Submit a pull request to the repository in the 'theforeman' 60 | organization. 61 | 62 | The long version 63 | ================ 64 | 65 | 1. Make separate commits for logically separate changes. 66 | 67 | Please break your commits down into logically consistent units 68 | which include new or changed tests relevant to the rest of the 69 | change. The goal of doing this is to make the diff easier to 70 | read for whoever is reviewing your code. In general, the easier 71 | your diff is to read, the more likely someone will be happy to 72 | review it and get it into the code base. 73 | 74 | If you are going to refactor a piece of code, please do so as a 75 | separate commit from your feature or bug fix changes. 76 | 77 | If you have many commits to fix one issue, use `git rebase` or 78 | `git commit --amend` to combine them into a single commit. 79 | 80 | We also really appreciate changes that include tests to make 81 | sure the bug is not re-introduced, and that the feature is not 82 | accidentally broken. 83 | 84 | Describe the technical detail of the change(s). If your 85 | description starts to get too long, that is a good sign that you 86 | probably need to split up your commit into more finely grained 87 | pieces. 88 | 89 | Commits which plainly describe the things which help 90 | reviewers check the patch and future developers understand the 91 | code are much more likely to be merged in with a minimum of 92 | bike-shedding or requested changes. Ideally, the commit message 93 | would include information, and be in a form suitable for 94 | inclusion in the release notes for the version of Puppet that 95 | includes them. 96 | 97 | Please also check that you are not introducing any trailing 98 | whitespace or other "whitespace errors". You can do this by 99 | running "git diff --check" on your changes before you commit. 100 | 101 | 2. Sending your patches 102 | 103 | To submit your changes via a GitHub pull request, we _highly_ 104 | recommend that you have them on a topic branch, instead of 105 | directly on "master". 106 | It makes things much easier to keep track of, especially if 107 | you decide to work on another thing before your first change 108 | is merged in. 109 | 110 | GitHub has some pretty good 111 | [general documentation](https://help.github.com/) on using 112 | their site. They also have documentation on 113 | [creating pull requests](https://help.github.com/send-pull-requests/). 114 | 115 | In general, after pushing your topic branch up to your 116 | repository on GitHub, you can switch to the branch in the 117 | GitHub UI and click "Pull Request" towards the top of the page 118 | in order to open a pull request. 119 | 120 | 121 | 3. Update the related GitHub issue. 122 | 123 | If there is a GitHub issue associated with the change you 124 | submitted, then you should update the ticket to include the 125 | location of your branch, along with any other commentary you 126 | may wish to make. 127 | 128 | Testing 129 | ======= 130 | 131 | Getting Started 132 | --------------- 133 | 134 | Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby 135 | package manager such as [bundler](https://bundler.io/) what Ruby packages, 136 | or Gems, are required to build, develop, and test this software. 137 | 138 | **Prerequisites** 139 | 1. Make sure you have [bundler installed](https://bundler.io/#getting-started) 140 | on your system. If you are using Fedora, you can get `bundler` using 141 | ```shell 142 | sudo dnf install rubygem-bundler 143 | ``` 144 | 2. If you are using Fedora, you may need these additional packages 145 | ```shell 146 | sudo dnf install -y ruby-devel redhat-rpm-config 147 | ``` 148 | 149 | Now, go to the root directory of this project and use `bundler` to install all 150 | dependencies needed for this project by running 151 | 152 | ```console 153 | $ bundle install 154 | Fetching gem metadata from https://rubygems.org/........ 155 | Fetching gem metadata from https://rubygems.org/.. 156 | Using rake (10.1.0) 157 | Using builder (3.2.2) 158 | -- 8><-- many more --><8 -- 159 | Using rspec-system-puppet (2.2.0) 160 | Using serverspec (0.6.3) 161 | Using rspec-system-serverspec (1.0.0) 162 | Using bundler (1.3.5) 163 | Your bundle is complete! 164 | Use `bundle show [gemname]` to see where a bundled gem is installed. 165 | ``` 166 | 167 | NOTE some systems may require you to run this command with sudo. 168 | 169 | If you already have those gems installed, make sure they are up-to-date: 170 | 171 | ```shell 172 | bundle update 173 | ``` 174 | 175 | With all dependencies in place and up-to-date we can now run the tests: 176 | 177 | ```shell 178 | rake spec 179 | ``` 180 | 181 | This will execute all the [rspec tests](http://rspec-puppet.com/) tests under 182 | [spec/defines](./spec/defines), [spec/classes](./spec/classes), and so on. 183 | rspec tests may have the same kind of dependencies as the module they are 184 | testing. While the module defines in its [metadata.json](./metadata.json), 185 | rspec tests define them in [.fixtures.yml](./fixtures.yml). 186 | 187 | To run specific tests, use the spec test file name and a filter like: 188 | 189 | ```shell 190 | bundle exec rspec spec/classes/foreman_spec.rb -e 'should restart passenger' 191 | ``` 192 | More filter info available [here](https://relishapp.com/rspec/rspec-core/v/3-9/docs/command-line/example-option) 193 | 194 | To run OS specific tests: 195 | 196 | ```shell 197 | SPEC_FACTS_OS=redhat-8-x86_64 bundle exec rspec spec/classes/foreman_spec.rb 198 | ``` 199 | 200 | If you have more than one version of `redhat` OS specified in metadata.json, 201 | you can run them all like: 202 | 203 | ```shell 204 | SPEC_FACTS_OS=redhat bundle exec rspec spec/classes/foreman_spec.rb 205 | ``` 206 | For more information on running the tests, see [rspec-puppet-facts](https://github.com/mcanevet/rspec-puppet-facts) 207 | and specifically the [section for running tests](https://github.com/mcanevet/rspec-puppet-facts#running-your-tests). 208 | 209 | Writing Tests 210 | ------------- 211 | 212 | Our tests use [rspec-puppet](http://rspec-puppet.com/) to check that classes 213 | and defined types work when compiled with Puppet itself. Ideally, we want to 214 | test the smallest logical units possible (i.e. a single class, not the whole 215 | module), which helps with speed and reduces work when changing other parts of 216 | the module. 217 | 218 | By writing tests we ensure that future versions of the module don't introduce 219 | regressions, and that we find issues sooner (in this project) rather than later 220 | (when used in a Foreman installation). 221 | 222 | Each class has its own file within spec/classes/, ending with "_spec.rb" and 223 | inside each test file is a section for each test scenario ("describe"). 224 | 225 | A typical rspec-puppet test for a new class parameter would perhaps start with 226 | defining a set of parameters to pass into the class: 227 | 228 | describe 'with colour parameter' do 229 | let :params do 230 | {:colour => 'red'} 231 | end 232 | end 233 | 234 | The test then has to check for the presence or absence of certain Puppet 235 | resources with or without certain properties and relationships. e.g. a test 236 | for a service class would ensure the service resource is present, with the 237 | right name and the right ensure/enable properties. 238 | 239 | describe 'with colour parameter' do 240 | let :params do 241 | {:colour => 'red'} 242 | end 243 | 244 | it 'should configure colour' do 245 | should contain_file('/etc/service.conf').with_content('colour: red') 246 | end 247 | end 248 | 249 | More advanced topics: 250 | 251 | * Use rspec-puppet-facts loops: some tests use a loop with "on_supported_os" 252 | to test the class under every OS supported in metadata.json. 253 | * Test presence of inter-resource require/notify relationships. 254 | 255 | 256 | If you have commit access to the repository 257 | =========================================== 258 | 259 | Even if you have commit access to the repository, you will still need to 260 | go through the process above, and have someone else review and merge 261 | in your changes. The rule is that all changes must be reviewed by a 262 | developer on the project (that did not write the code) to ensure that 263 | all changes go through a code review process. 264 | 265 | Having someone other than the author of the topic branch recorded as 266 | performing the merge is the record that they performed the code 267 | review. 268 | 269 | 270 | Additional Resources 271 | ==================== 272 | 273 | * [Support and contact details](https://theforeman.org/support.html) 274 | 275 | * [General Foreman contribution details](https://theforeman.org/contribute.html) 276 | 277 | * [General GitHub documentation](https://help.github.com/) 278 | 279 | * [GitHub pull request documentation](https://help.github.com/send-pull-requests/) 280 | 281 | 282 | Modulesync 283 | ========== 284 | 285 | Various files, including this one, are 286 | [modulesynced](https://github.com/voxpupuli/modulesync) using 287 | [foreman-installer-modulesync](https://github.com/theforeman/foreman-installer-modulesync) 288 | configuration. Changes should be made over there and then synced to 289 | all [managed 290 | modules](https://github.com/theforeman/foreman-installer-modulesync/blob/master/managed_modules.yml). 291 | -------------------------------------------------------------------------------- /manifests/server/puppetserver.pp: -------------------------------------------------------------------------------- 1 | # Configures the puppetserver jvm configuration file using augeas. 2 | # 3 | # @api private 4 | # 5 | # @param java_bin 6 | # Path to the java executable to use 7 | # 8 | # @param config 9 | # Path to the jvm configuration file. 10 | # This file is usually either /etc/default/puppetserver or 11 | # /etc/sysconfig/puppetserver depending on your *nix flavor. 12 | # 13 | # @param jvm_min_heap_size 14 | # Translates into the -Xms option and is added to the JAVA_ARGS 15 | # 16 | # @param jvm_max_heap_size 17 | # Translates into the -Xmx option and is added to the JAVA_ARGS 18 | # 19 | # @param jvm_extra_args 20 | # Custom options to pass through to the java binary. These get added to 21 | # the end of the JAVA_ARGS variable 22 | # 23 | # @param jvm_cli_args 24 | # Custom options to pass through to the java binary when using a 25 | # puppetserver subcommand, (eg puppetserver gem). These get used 26 | # in the JAVA_ARGS_CLI variable. 27 | # 28 | # @param server_puppetserver_dir 29 | # Puppetserver config directory 30 | # 31 | # @param server_puppetserver_vardir 32 | # Puppetserver var directory 33 | # 34 | # @param server_jruby_gem_home 35 | # Puppetserver jruby gemhome 36 | # 37 | # @param server_environment_vars 38 | # Puppetserver hash of environment vars 39 | # 40 | # @param server_cipher_suites 41 | # Puppetserver array of acceptable ciphers 42 | # 43 | # @param server_ssl_protocols 44 | # Puppetserver array of acceptable ssl protocols 45 | # 46 | # @param server_max_active_instances 47 | # Puppetserver number of max jruby instances 48 | # 49 | # @param server_max_requests_per_instance 50 | # Puppetserver number of max requests per jruby instance 51 | # 52 | # @param server_max_queued_requests 53 | # The maximum number of requests that may be queued waiting 54 | # to borrow a JRuby from the pool. 55 | # 56 | # @param server_max_retry_delay 57 | # Sets the upper limit for the random sleep set as a Retry-After 58 | # header on 503 responses returned when max-queued-requests is enabled. 59 | # 60 | # @param server_multithreaded 61 | # Configures the puppetserver to use multithreaded jruby. 62 | # 63 | # @param disable_fips 64 | # Disables FIPS support within the JVM 65 | # 66 | # @example configure memory for java < 8 67 | # class {'::puppet::server::puppetserver': 68 | # jvm_min_heap_size => '1G', 69 | # jvm_max_heap_size => '3G', 70 | # jvm_extra_args => '-XX:MaxPermSize=256m', 71 | # } 72 | # 73 | class puppet::server::puppetserver ( 74 | Optional[Pattern[/^[\d]\.[\d]+\.[\d]+$/]] $puppetserver_version = $puppet::server::puppetserver_version, 75 | String $config = $puppet::server::jvm_config, 76 | Optional[Stdlib::Absolutepath] $java_bin = $puppet::server::jvm_java_bin, 77 | Variant[String, Array[String]] $jvm_extra_args = $puppet::server::real_jvm_extra_args, 78 | Optional[String] $jvm_cli_args = $puppet::server::jvm_cli_args, 79 | Pattern[/^[0-9]+[kKmMgG]$/] $jvm_min_heap_size = $puppet::server::jvm_min_heap_size, 80 | Pattern[/^[0-9]+[kKmMgG]$/] $jvm_max_heap_size = $puppet::server::jvm_max_heap_size, 81 | Stdlib::Absolutepath $server_puppetserver_dir = $puppet::server::puppetserver_dir, 82 | Stdlib::Absolutepath $server_puppetserver_vardir = $puppet::server::puppetserver_vardir, 83 | Optional[Stdlib::Absolutepath] $server_puppetserver_rundir = $puppet::server::puppetserver_rundir, 84 | Optional[Stdlib::Absolutepath] $server_puppetserver_logdir = $puppet::server::puppetserver_logdir, 85 | Optional[Stdlib::Absolutepath] $server_jruby_gem_home = $puppet::server::jruby_gem_home, 86 | Hash[String, String] $server_environment_vars = $puppet::server::server_environment_vars, 87 | Array[String] $server_ruby_load_paths = $puppet::server::ruby_load_paths, 88 | Array[String] $server_cipher_suites = $puppet::server::cipher_suites, 89 | Integer[1] $server_max_active_instances = $puppet::server::max_active_instances, 90 | Integer[0] $server_max_requests_per_instance = $puppet::server::max_requests_per_instance, 91 | Integer[0] $server_max_queued_requests = $puppet::server::max_queued_requests, 92 | Integer[0] $server_max_retry_delay = $puppet::server::max_retry_delay, 93 | Boolean $server_multithreaded = $puppet::server::multithreaded, 94 | Array[String] $server_ssl_protocols = $puppet::server::ssl_protocols, 95 | Stdlib::Absolutepath $server_ssl_ca_crl = $puppet::server::ssl_ca_crl, 96 | Stdlib::Absolutepath $server_ssl_ca_cert = $puppet::server::ssl_ca_cert, 97 | Stdlib::Absolutepath $server_ssl_cert = $puppet::server::ssl_cert, 98 | Stdlib::Absolutepath $server_ssl_cert_key = $puppet::server::ssl_cert_key, 99 | Variant[Boolean, Stdlib::Absolutepath] $server_ssl_chain = $puppet::server::ssl_chain, 100 | Boolean $server_crl_enable = $puppet::server::crl_enable_real, 101 | String $server_ip = $puppet::server::ip, 102 | Stdlib::Port $server_port = $puppet::server::port, 103 | Boolean $server_http = $puppet::server::http, 104 | Stdlib::Port $server_http_port = $puppet::server::http_port, 105 | Boolean $server_ca = $puppet::server::ca, 106 | String $server_dir = $puppet::server::dir, 107 | Stdlib::Absolutepath $codedir = $puppet::server::codedir, 108 | Integer[0] $server_idle_timeout = $puppet::server::idle_timeout, 109 | Integer[0] $server_web_idle_timeout = $puppet::server::web_idle_timeout, 110 | Integer[0] $server_connect_timeout = $puppet::server::connect_timeout, 111 | Boolean $server_ca_auth_required = $puppet::server::ca_auth_required, 112 | Boolean $server_ca_client_self_delete = $puppet::server::ca_client_self_delete, 113 | Array[String] $server_ca_client_allowlist = $puppet::server::ca_client_allowlist, 114 | Array[String] $server_admin_api_allowlist = $puppet::server::admin_api_allowlist, 115 | Boolean $server_check_for_updates = $puppet::server::check_for_updates, 116 | Boolean $server_environment_class_cache_enabled = $puppet::server::environment_class_cache_enabled, 117 | Optional[Boolean] $server_metrics = $puppet::server::puppetserver_metrics, 118 | Boolean $server_profiler = $puppet::server::puppetserver_profiler, 119 | Boolean $server_telemetry = pick($puppet::server::puppetserver_telemetry, false), 120 | Boolean $metrics_jmx_enable = $puppet::server::metrics_jmx_enable, 121 | Boolean $metrics_graphite_enable = $puppet::server::metrics_graphite_enable, 122 | String $metrics_graphite_host = $puppet::server::metrics_graphite_host, 123 | Stdlib::Port $metrics_graphite_port = $puppet::server::metrics_graphite_port, 124 | String $metrics_server_id = $puppet::server::metrics_server_id, 125 | Integer $metrics_graphite_interval = $puppet::server::metrics_graphite_interval, 126 | Optional[Array] $metrics_allowed = $puppet::server::metrics_allowed, 127 | Boolean $server_experimental = $puppet::server::puppetserver_experimental, 128 | Optional[String[1]] $server_auth_template = $puppet::server::puppetserver_auth_template, 129 | Array[String] $server_trusted_agents = $puppet::server::puppetserver_trusted_agents, 130 | Array[Hash] $server_trusted_certificate_extensions = $puppet::server::puppetserver_trusted_certificate_extensions, 131 | Boolean $allow_header_cert_info = $puppet::server::allow_header_cert_info, 132 | Optional[Enum['off', 'jit', 'force']] $compile_mode = $puppet::server::compile_mode, 133 | Optional[Integer[1]] $acceptor_threads = $puppet::server::acceptor_threads, 134 | Optional[Integer[1]] $selector_threads = $puppet::server::selector_threads, 135 | Optional[Integer[1]] $ssl_acceptor_threads = $puppet::server::ssl_acceptor_threads, 136 | Optional[Integer[1]] $ssl_selector_threads = $puppet::server::ssl_selector_threads, 137 | Optional[Integer[1]] $max_threads = $puppet::server::max_threads, 138 | Boolean $ca_allow_sans = $puppet::server::ca_allow_sans, 139 | Boolean $ca_allow_auth_extensions = $puppet::server::ca_allow_auth_extensions, 140 | Boolean $ca_enable_infra_crl = $puppet::server::ca_enable_infra_crl, 141 | Boolean $server_ca_allow_auto_renewal = $puppet::server::server_ca_allow_auto_renewal, 142 | String $server_ca_allow_auto_renewal_cert_ttl = $puppet::server::server_ca_allow_auto_renewal_cert_ttl, 143 | Optional[Integer[1]] $max_open_files = $puppet::server::max_open_files, 144 | Optional[Stdlib::Absolutepath] $versioned_code_id = $puppet::server::versioned_code_id, 145 | Optional[Stdlib::Absolutepath] $versioned_code_content = $puppet::server::versioned_code_content, 146 | Boolean $disable_fips = $facts['os']['family'] == 'RedHat', 147 | Array[String[1]] $jolokia_metrics_allowlist = $puppet::server::jolokia_metrics_allowlist, 148 | ) { 149 | include puppet::server 150 | 151 | # For Puppetserver, certain configuration parameters are version specific. 152 | # We need a method to determine what version is installed. 153 | $real_puppetserver_version = pick($puppetserver_version, '8.0.0') 154 | 155 | if $java_bin { 156 | $_java_bin = $java_bin 157 | } else { 158 | # Follows logic that https://github.com/puppetlabs/ezbake/pull/627 suggests, but takes it a 159 | # step further by also ensuring EL 8 has Java 17 160 | $_java_bin = case $facts['os']['family'] { 161 | 'RedHat': { 162 | $facts['os']['release']['major'] ? { 163 | /^([89])$/ => '/usr/lib/jvm/jre-17/bin/java', 164 | default => '/usr/bin/java' 165 | } 166 | } 167 | default: { 168 | '/usr/bin/java' 169 | } 170 | } 171 | } 172 | 173 | $jvm_heap_arr = ["-Xms${jvm_min_heap_size}", "-Xmx${jvm_max_heap_size}"] 174 | if $disable_fips { 175 | $jvm_cmd_arr = $jvm_heap_arr + ['-Dcom.redhat.fips=false', $jvm_extra_args] 176 | } else { 177 | $jvm_cmd_arr = $jvm_heap_arr + [$jvm_extra_args] 178 | } 179 | $jvm_cmd = strip(join(flatten($jvm_cmd_arr), ' ')) 180 | 181 | if $facts['os']['family'] == 'FreeBSD' { 182 | $server_gem_paths = ['${jruby-puppet.gem-home}', "\"${server_puppetserver_vardir}/vendored-jruby-gems\"", sprintf('"%s"', regsubst($facts['ruby']['sitedir'], 'site_ruby', 'gems'))] # lint:ignore:single_quote_string_with_variables 183 | augeas { 'puppet::server::puppetserver::jvm': 184 | context => '/files/etc/rc.conf', 185 | changes => ["set puppetserver_java_opts '\"${jvm_cmd}\"'"], 186 | } 187 | } elsif $facts['os']['family'] == 'Debian' and !$puppet::params::aio_package { 188 | $server_gem_paths = ['${jruby-puppet.gem-home}', '/usr/lib/puppetserver/vendored-jruby-gems'] # lint:ignore:single_quote_string_with_variables 189 | } else { 190 | if $jvm_cli_args { 191 | $changes = [ 192 | "set JAVA_ARGS '\"${jvm_cmd}\"'", 193 | "set JAVA_BIN ${_java_bin}", 194 | "set JAVA_ARGS_CLI '\"${jvm_cli_args}\"'", 195 | ] 196 | } else { 197 | $changes = [ 198 | "set JAVA_ARGS '\"${jvm_cmd}\"'", 199 | "set JAVA_BIN ${_java_bin}", 200 | ] 201 | } 202 | augeas { 'puppet::server::puppetserver::jvm': 203 | lens => 'Shellvars.lns', 204 | incl => $config, 205 | context => "/files${config}", 206 | changes => $changes, 207 | } 208 | 209 | $bootstrap_paths = "${server_puppetserver_dir}/services.d/,/opt/puppetlabs/server/apps/puppetserver/config/services.d/" 210 | 211 | $server_gem_paths = ['${jruby-puppet.gem-home}', "\"${server_puppetserver_vardir}/vendored-jruby-gems\"", "\"/opt/puppetlabs/puppet/lib/ruby/vendor_gems\""] # lint:ignore:single_quote_string_with_variables 212 | 213 | augeas { 'puppet::server::puppetserver::bootstrap': 214 | lens => 'Shellvars.lns', 215 | incl => $config, 216 | context => "/files${config}", 217 | changes => "set BOOTSTRAP_CONFIG '\"${bootstrap_paths}\"'", 218 | } 219 | 220 | augeas { 'puppet::server::puppetserver::jruby_jar': 221 | lens => 'Shellvars.lns', 222 | incl => $config, 223 | context => "/files${config}", 224 | changes => 'rm JRUBY_JAR', 225 | } 226 | 227 | $ensure_max_open_files = $max_open_files ? { 228 | undef => 'absent', 229 | default => 'present', 230 | } 231 | if $facts['service_provider'] == 'systemd' { 232 | systemd::dropin_file { 'puppetserver.service-limits.conf': 233 | ensure => $ensure_max_open_files, 234 | filename => 'limits.conf', 235 | unit => 'puppetserver.service', 236 | content => "[Service]\nLimitNOFILE=${max_open_files}\n", 237 | } 238 | 239 | # https://github.com/puppetlabs/ezbake/pull/623 240 | systemd::dropin_file { 'puppetserver.service-privatetmp.conf': 241 | ensure => present, 242 | filename => 'privatetmp.conf', 243 | unit => 'puppetserver.service', 244 | content => "[Service]\nPrivateTmp=true\n", 245 | } 246 | } else { 247 | file_line { 'puppet::server::puppetserver::max_open_files': 248 | ensure => $ensure_max_open_files, 249 | path => $config, 250 | line => "ulimit -n ${max_open_files}", 251 | match => '^ulimit\ -n', 252 | } 253 | } 254 | } 255 | 256 | $servicesd = "${server_puppetserver_dir}/services.d" 257 | file { $servicesd: 258 | ensure => directory, 259 | } 260 | file { "${servicesd}/ca.cfg": 261 | ensure => file, 262 | content => template('puppet/server/puppetserver/services.d/ca.cfg.erb'), 263 | } 264 | 265 | file { "${server_puppetserver_dir}/conf.d/ca.conf": 266 | ensure => file, 267 | content => template('puppet/server/puppetserver/conf.d/ca.conf.erb'), 268 | } 269 | 270 | file { "${server_puppetserver_dir}/conf.d/puppetserver.conf": 271 | ensure => file, 272 | content => template('puppet/server/puppetserver/conf.d/puppetserver.conf.erb'), 273 | } 274 | 275 | $auth_template = pick($server_auth_template, 'puppet/server/puppetserver/conf.d/auth.conf.erb') 276 | file { "${server_puppetserver_dir}/conf.d/auth.conf": 277 | ensure => file, 278 | content => template($auth_template), 279 | } 280 | 281 | file { "${server_puppetserver_dir}/conf.d/webserver.conf": 282 | ensure => file, 283 | content => template('puppet/server/puppetserver/conf.d/webserver.conf.erb'), 284 | } 285 | 286 | file { "${server_puppetserver_dir}/conf.d/product.conf": 287 | ensure => file, 288 | content => template('puppet/server/puppetserver/conf.d/product.conf.erb'), 289 | } 290 | 291 | file { "${server_puppetserver_dir}/conf.d/metrics.conf": 292 | ensure => 'file', 293 | content => template('puppet/server/puppetserver/conf.d/metrics.conf.erb'), 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /templates/server/puppetserver/conf.d/auth.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Managed by Puppet 3 | # 4 | authorization: { 5 | version: 1 6 | allow-header-cert-info: <%= @server_http || @allow_header_cert_info %> 7 | rules: [ 8 | { 9 | # Allow nodes to retrieve their own catalog 10 | match-request: { 11 | path: "^/puppet/v3/catalog/([^/]+)$" 12 | type: regex 13 | method: [get, post] 14 | } 15 | <%= scope.call_function( 16 | 'stdlib::to_json_pretty', 17 | [ 18 | {'allow' => @server_trusted_agents + ['$1'] + @server_trusted_certificate_extensions.map { |extension| { 'extensions' => extension } } } 19 | ] 20 | ).lines.to_a[1..-2].map{ |line| " #{line}"}.join 21 | -%> 22 | sort-order: 500 23 | name: "puppetlabs v3 catalog from agents" 24 | }, 25 | { 26 | # Allow services to retrieve catalogs on behalf of others 27 | match-request: { 28 | path: "^/puppet/v4/catalog/?$" 29 | type: regex 30 | method: post 31 | } 32 | <%- if @server_trusted_agents.empty? && @server_trusted_certificate_extensions.empty? -%> 33 | deny: "*" 34 | <%- else -%> 35 | <%= scope.call_function( 36 | 'stdlib::to_json_pretty', 37 | [ 38 | {'allow' => @server_trusted_agents + @server_trusted_certificate_extensions.map { |extension| { 'extensions' => extension } } } 39 | ] 40 | ).lines.to_a[1..-2].map{ |line| " #{line}"}.join 41 | -%> 42 | <%- end -%> 43 | sort-order: 500 44 | name: "puppetlabs v4 catalog for services" 45 | }, 46 | { 47 | # Allow nodes to retrieve the certificate they requested earlier 48 | match-request: { 49 | path: "/puppet-ca/v1/certificate/" 50 | type: path 51 | method: get 52 | } 53 | allow-unauthenticated: true 54 | sort-order: 500 55 | name: "puppetlabs certificate" 56 | }, 57 | { 58 | # Allow all nodes to access the certificate revocation list 59 | match-request: { 60 | path: "/puppet-ca/v1/certificate_revocation_list/ca" 61 | type: path 62 | method: get 63 | } 64 | allow-unauthenticated: true 65 | sort-order: 500 66 | name: "puppetlabs crl" 67 | }, 68 | { 69 | # Allow nodes to request a new certificate 70 | match-request: { 71 | path: "/puppet-ca/v1/certificate_request" 72 | type: path 73 | method: [get, put] 74 | } 75 | allow-unauthenticated: true 76 | sort-order: 500 77 | name: "puppetlabs csr" 78 | }, 79 | <%- if @server_ca -%> 80 | <%- if scope.function_versioncmp([@real_puppetserver_version, '8.0.0']) >= 0 -%> 81 | { 82 | # Allow nodes to renew their certificate 83 | match-request: { 84 | path: "/puppet-ca/v1/certificate_renewal" 85 | type: path 86 | method: post 87 | } 88 | # this endpoint should never be unauthenticated, as it requires the cert to be provided. 89 | allow: "*" 90 | sort-order: 500 91 | name: "puppetlabs certificate renewal" 92 | }, 93 | <%- end -%> 94 | { 95 | # Allow the CA CLI to access the certificate_status endpoint 96 | match-request: { 97 | path: "/puppet-ca/v1/certificate_status" 98 | type: path 99 | method: [get, put, delete] 100 | } 101 | <%- if @server_ca_auth_required == false -%> 102 | allow-unauthenticated: true 103 | <%- else -%> 104 | allow: [ 105 | <%- @server_ca_client_allowlist.each do |client| -%> 106 | "<%= client %>", 107 | <%- end -%> 108 | { 109 | extensions: { 110 | pp_cli_auth: "true" 111 | } 112 | } 113 | ] 114 | <%- end -%> 115 | sort-order: 500 116 | name: "puppetlabs cert status" 117 | }, 118 | { 119 | match-request: { 120 | path: "^/puppet-ca/v1/certificate_revocation_list$" 121 | type: regex 122 | method: put 123 | } 124 | allow: { 125 | extensions: { 126 | pp_cli_auth: "true" 127 | } 128 | } 129 | sort-order: 500 130 | name: "puppetlabs CRL update" 131 | }, 132 | { 133 | # Allow the CA CLI to access the certificate_statuses endpoint 134 | match-request: { 135 | path: "/puppet-ca/v1/certificate_statuses" 136 | type: path 137 | method: get 138 | } 139 | <%- if @server_ca_auth_required == false -%> 140 | allow-unauthenticated: true 141 | <%- else -%> 142 | allow: [ 143 | <%- @server_ca_client_allowlist.each do |client| -%> 144 | "<%= client %>", 145 | <%- end -%> 146 | { 147 | extensions: { 148 | pp_cli_auth: "true" 149 | } 150 | } 151 | ] 152 | <%- end -%> 153 | sort-order: 500 154 | name: "puppetlabs cert statuses" 155 | }, 156 | <%- if @server_ca_client_self_delete -%> 157 | { 158 | name: "Allow nodes to delete their own certificates", 159 | match-request: { 160 | path: "^/puppet-ca/v1/certificate(_status|_request)?/([^/]+)$" 161 | type: regex 162 | method: [delete] 163 | }, 164 | allow: [ 165 | "$2", 166 | <%- @server_admin_api_allowlist.each do |client| -%> 167 | "<%= client %>", 168 | <%- end -%> 169 | { 170 | extensions: { 171 | pp_cli_auth: "true" 172 | } 173 | } 174 | ] 175 | sort-order: 500 176 | }, 177 | <%- end -%> 178 | <%- end -%> 179 | { 180 | # Allow authenticated access to the CA expirations endpoint 181 | match-request: { 182 | path: "/puppet-ca/v1/expirations" 183 | type: path 184 | method: get 185 | } 186 | allow: "*" 187 | sort-order: 500 188 | name: "puppetlabs CA cert and CRL expirations" 189 | }, 190 | { 191 | # Allow the CA CLI to access the certificate clean endpoint 192 | match-request: { 193 | path: "/puppet-ca/v1/clean" 194 | type: path 195 | method: put 196 | } 197 | allow: { 198 | extensions: { 199 | pp_cli_auth: "true" 200 | } 201 | } 202 | sort-order: 500 203 | name: "puppetlabs cert clean" 204 | }, 205 | { 206 | # Allow the CA CLI to access the certificate sign endpoint 207 | match-request: { 208 | path: "/puppet-ca/v1/sign" 209 | type: path 210 | method: post 211 | } 212 | allow: [ 213 | <%- @server_ca_client_allowlist.each do |client| -%> 214 | "<%= client %>", 215 | <%- end -%> 216 | { 217 | extensions: { 218 | pp_cli_auth: "true" 219 | } 220 | } 221 | ] 222 | sort-order: 500 223 | name: "puppetlabs cert sign" 224 | }, 225 | { 226 | # Allow the CA CLI to access the certificate sign all endpoint 227 | match-request: { 228 | path: "/puppet-ca/v1/sign/all" 229 | type: path 230 | method: post 231 | } 232 | allow: [ 233 | <%- @server_ca_client_allowlist.each do |client| -%> 234 | "<%= client %>", 235 | <%- end -%> 236 | { 237 | extensions: { 238 | pp_cli_auth: "true" 239 | } 240 | } 241 | ] 242 | sort-order: 500 243 | name: "puppetlabs cert sign all" 244 | }, 245 | { 246 | # Allow unauthenticated access to the status service endpoint 247 | match-request: { 248 | path: "/status/v1/services" 249 | type: path 250 | method: get 251 | } 252 | allow-unauthenticated: true 253 | sort-order: 500 254 | name: "puppetlabs status service - full" 255 | }, 256 | { 257 | match-request: { 258 | path: "/status/v1/simple" 259 | type: path 260 | method: get 261 | } 262 | allow-unauthenticated: true 263 | sort-order: 500 264 | name: "puppetlabs status service - simple" 265 | }, 266 | { 267 | match-request: { 268 | path: "/puppet-admin-api/v1/environment-cache" 269 | type: path 270 | method: delete 271 | } 272 | allow: [ 273 | <%- @server_admin_api_allowlist.each do |client| -%> 274 | "<%= client %>", 275 | <%- end -%> 276 | ] 277 | sort-order: 200 278 | name: "environment-cache" 279 | }, 280 | { 281 | match-request: { 282 | path: "/puppet-admin-api/v1/jruby-pool" 283 | type: path 284 | method: delete 285 | } 286 | allow: [ 287 | <%- @server_admin_api_allowlist.each do |client| -%> 288 | "<%= client %>", 289 | <%- end -%> 290 | ] 291 | sort-order: 200 292 | name: "jruby-pool" 293 | }, 294 | { 295 | match-request: { 296 | path: "/puppet/v3/environments" 297 | type: path 298 | method: get 299 | } 300 | allow: "*" 301 | sort-order: 500 302 | name: "puppetlabs environments" 303 | }, 304 | { 305 | match-request: { 306 | path: "/puppet/v3/environment_classes" 307 | type: path 308 | method: get 309 | } 310 | allow: "*" 311 | sort-order: 500 312 | name: "puppetlabs environment classes" 313 | }, 314 | { 315 | # Allow nodes to access all file_bucket_files. Note that access for 316 | # the 'delete' method is forbidden by Puppet regardless of the 317 | # configuration of this rule. 318 | match-request: { 319 | path: "/puppet/v3/file_bucket_file" 320 | type: path 321 | method: [get, head, post, put] 322 | } 323 | allow: "*" 324 | sort-order: 500 325 | name: "puppetlabs file bucket file" 326 | }, 327 | { 328 | # Allow nodes to access all file_content. Note that access for the 329 | # 'delete' method is forbidden by Puppet regardless of the 330 | # configuration of this rule. 331 | match-request: { 332 | path: "/puppet/v3/file_content" 333 | type: path 334 | method: [get, post] 335 | } 336 | allow: "*" 337 | sort-order: 500 338 | name: "puppetlabs file content" 339 | }, 340 | { 341 | # Allow nodes to access all file_metadata. Note that access for the 342 | # 'delete' method is forbidden by Puppet regardless of the 343 | # configuration of this rule. 344 | match-request: { 345 | path: "/puppet/v3/file_metadata" 346 | type: path 347 | method: [get, post] 348 | } 349 | allow: "*" 350 | sort-order: 500 351 | name: "puppetlabs file metadata" 352 | }, 353 | { 354 | # Allow nodes to retrieve only their own node definition 355 | match-request: { 356 | path: "^/puppet/v3/node/([^/]+)$" 357 | type: regex 358 | method: get 359 | } 360 | allow: "$1" 361 | sort-order: 500 362 | name: "puppetlabs node" 363 | }, 364 | { 365 | # Allow nodes to store only their own reports 366 | match-request: { 367 | path: "^/puppet/v3/report/([^/]+)$" 368 | type: regex 369 | method: put 370 | } 371 | allow: "$1" 372 | sort-order: 500 373 | name: "puppetlabs report" 374 | }, 375 | { 376 | # Allow nodes to update their own facts 377 | match-request: { 378 | path: "^/puppet/v3/facts/([^/]+)$" 379 | type: regex 380 | method: put 381 | } 382 | allow: "$1" 383 | sort-order: 500 384 | name: "puppetlabs facts" 385 | }, 386 | { 387 | match-request: { 388 | path: "/puppet/v3/static_file_content" 389 | type: path 390 | method: get 391 | } 392 | allow: "*" 393 | sort-order: 500 394 | name: "puppetlabs static file content" 395 | }, 396 | { 397 | match-request: { 398 | path: "/puppet/v3/tasks" 399 | type: path 400 | } 401 | allow: "*" 402 | sort-order: 500 403 | name: "puppet tasks information" 404 | }, 405 | <%- if @server_experimental -%> 406 | { 407 | # Allow all users access to the experimental endpoint 408 | # which currently only provides a dashboard web ui. 409 | match-request: { 410 | path: "/puppet/experimental" 411 | type: path 412 | } 413 | allow-unauthenticated: true 414 | sort-order: 500 415 | name: "puppetlabs experimental" 416 | }, 417 | <%- end -%> 418 | <%- unless @jolokia_metrics_allowlist.empty? -%> 419 | { 420 | match-request: { 421 | path: "/metrics/v2" 422 | type: path 423 | } 424 | allow: [ 425 | <%- @jolokia_metrics_allowlist.each do |client| -%> 426 | "<%= client %>", 427 | <%- end -%> 428 | ] 429 | sort-order: 500 430 | name: "jolokia metrics" 431 | }, 432 | <%- end -%> 433 | { 434 | # Deny everything else. This ACL is not strictly 435 | # necessary, but illustrates the default policy 436 | match-request: { 437 | path: "/" 438 | type: path 439 | } 440 | deny: "*" 441 | sort-order: 999 442 | name: "puppetlabs deny all" 443 | } 444 | ] 445 | } 446 | -------------------------------------------------------------------------------- /manifests/params.pp: -------------------------------------------------------------------------------- 1 | # Default parameters 2 | # @api private 3 | class puppet::params { 4 | # Basic config 5 | $version = 'present' 6 | $manage_user = true 7 | $user = 'puppet' 8 | $group = 'puppet' 9 | $ip = '0.0.0.0' 10 | $agent_server_port = 8140 11 | $splay = false 12 | $splaylimit = 1800 13 | $runinterval = 1800 14 | $runmode = 'service' 15 | $report = true 16 | 17 | # Not defined here as the commands depend on module parameter "dir" 18 | $cron_cmd = undef 19 | $systemd_cmd = undef 20 | 21 | $agent_noop = false 22 | $show_diff = false 23 | $module_repository = undef 24 | $hiera_config = '$confdir/hiera.yaml' 25 | $usecacheonfailure = true 26 | $ca_server = undef 27 | $ca_port = undef 28 | $ca_crl_filepath = undef 29 | $certificate_revocation = undef 30 | $server_crl_enable = undef 31 | $prerun_command = undef 32 | $postrun_command = undef 33 | $server_compile_mode = undef 34 | $hostprivkey = '$privatekeydir/$certname.pem { mode = 640 }' 35 | $dns_alt_names = [] 36 | $use_srv_records = false 37 | $agent_default_schedules = false 38 | $agent_manage_environment = true 39 | 40 | $srv_domain = fact('networking.domain') 41 | 42 | # lint:ignore:puppet_url_without_modules 43 | $pluginsource = 'puppet:///plugins' 44 | $pluginfactsource = 'puppet:///pluginfacts' 45 | # lint:endignore 46 | $classfile = '$statedir/classes.txt' 47 | $syslogfacility = undef 48 | $environment = $server_facts['environment'] 49 | 50 | # aio_agent_version is a core fact that's empty on non-AIO 51 | $aio_package = fact('aio_agent_version') =~ String[1] 52 | 53 | $systemd_randomizeddelaysec = 0 54 | 55 | case $facts['os']['family'] { 56 | 'Windows' : { 57 | # Windows prefixes normal paths with the Data Directory's path and leaves 'puppet' off the end 58 | $dir_prefix = 'C:/ProgramData/PuppetLabs/puppet' 59 | $dir = "${dir_prefix}/etc" 60 | $codedir = "${dir_prefix}/etc" 61 | $logdir = "${dir_prefix}/var/log" 62 | $rundir = "${dir_prefix}/var/run" 63 | $ssldir = "${dir_prefix}/etc/ssl" 64 | $vardir = "${dir_prefix}/var" 65 | $sharedir = "${dir_prefix}/share" 66 | $bindir = "${dir_prefix}/bin" 67 | $root_group = undef 68 | $server_puppetserver_dir = undef 69 | $server_puppetserver_vardir = undef 70 | $server_puppetserver_rundir = undef 71 | $server_puppetserver_logdir = undef 72 | $server_ruby_load_paths = [] 73 | $server_jruby_gem_home = undef 74 | $puppetconf_mode = '0674' 75 | } 76 | 77 | /^(FreeBSD|DragonFly)$/ : { 78 | $dir = '/usr/local/etc/puppet' 79 | $codedir = '/usr/local/etc/puppet' 80 | $logdir = '/var/log/puppet' 81 | $rundir = '/var/run/puppet' 82 | $ssldir = '/var/puppet/ssl' 83 | $vardir = '/var/puppet' 84 | $sharedir = '/usr/local/share/puppet' 85 | $bindir = '/usr/local/bin' 86 | $root_group = undef 87 | $server_puppetserver_dir = '/usr/local/etc/puppetserver' 88 | $server_puppetserver_vardir = '/var/puppet/server/data/puppetserver' 89 | $server_puppetserver_rundir = '/var/run/puppetserver' 90 | $server_puppetserver_logdir = '/var/log/puppetserver' 91 | $ruby_gem_dir = regsubst($facts['ruby']['version'], '^(\d+\.\d+).*$', '/usr/local/lib/ruby/gems/\1/gems') 92 | $server_ruby_load_paths = [$facts['ruby']['sitedir'], "${ruby_gem_dir}/facter-${facts['facterversion']}/lib"] 93 | $server_jruby_gem_home = '/var/puppet/server/data/puppetserver/jruby-gems' 94 | $puppetconf_mode = '0644' 95 | } 96 | 97 | 'Archlinux' : { 98 | $dir = '/etc/puppetlabs/puppet' 99 | $codedir = '/etc/puppetlabs/code' 100 | $logdir = '/var/log/puppetlabs/puppet' 101 | $rundir = '/var/run/puppetlabs' 102 | $ssldir = '/etc/puppetlabs/puppet/ssl' 103 | $vardir = '/opt/puppetlabs/puppet/cache' 104 | $sharedir = '/opt/puppetlabs/puppet' 105 | $bindir = '/usr/bin' 106 | $root_group = undef 107 | $server_puppetserver_dir = undef 108 | $server_puppetserver_vardir = undef 109 | $server_puppetserver_rundir = undef 110 | $server_puppetserver_logdir = undef 111 | $server_ruby_load_paths = [] 112 | $server_jruby_gem_home = undef 113 | $puppetconf_mode = '0644' 114 | } 115 | 116 | default : { 117 | if $aio_package { 118 | $dir = '/etc/puppetlabs/puppet' 119 | $codedir = '/etc/puppetlabs/code' 120 | $logdir = '/var/log/puppetlabs/puppet' 121 | $rundir = '/var/run/puppetlabs' 122 | $ssldir = '/etc/puppetlabs/puppet/ssl' 123 | $vardir = '/opt/puppetlabs/puppet/cache' 124 | $sharedir = '/opt/puppetlabs/puppet' 125 | $bindir = '/opt/puppetlabs/bin' 126 | $server_puppetserver_dir = '/etc/puppetlabs/puppetserver' 127 | $server_puppetserver_vardir = '/opt/puppetlabs/server/data/puppetserver' 128 | $server_puppetserver_rundir = '/var/run/puppetlabs/puppetserver' 129 | $server_puppetserver_logdir = '/var/log/puppetlabs/puppetserver' 130 | $server_ruby_load_paths = ['/opt/puppetlabs/puppet/lib/ruby/vendor_ruby'] 131 | $server_jruby_gem_home = '/opt/puppetlabs/server/data/puppetserver/jruby-gems' 132 | } else { 133 | $dir = '/etc/puppet' 134 | $codedir = $facts['os']['family'] ? { 135 | 'Debian' => '/etc/puppet/code', 136 | default => '/etc/puppet', 137 | } 138 | $logdir = '/var/log/puppet' 139 | $rundir = '/var/run/puppet' 140 | $ssldir = '/var/lib/puppet/ssl' 141 | $vardir = '/var/lib/puppet' 142 | $sharedir = '/usr/share/puppet' 143 | $bindir = '/usr/bin' 144 | if $facts['os']['family'] == 'Debian' { 145 | $server_puppetserver_dir = '/etc/puppet/puppetserver' 146 | $server_puppetserver_vardir = '/var/lib/puppetserver' 147 | $server_puppetserver_rundir = '/run/puppetserver' 148 | $server_puppetserver_logdir = '/var/log/puppetserver' 149 | $server_ruby_load_paths = ['/usr/lib/puppetserver/ruby/vendor_ruby'] 150 | $server_jruby_gem_home = '/var/lib/puppetserver/jruby-gems' 151 | } else { 152 | $server_puppetserver_dir = '/etc/puppetserver' 153 | $server_puppetserver_vardir = $vardir 154 | $server_puppetserver_rundir = undef 155 | $server_puppetserver_logdir = undef 156 | $server_ruby_load_paths = [] 157 | $server_jruby_gem_home = '/var/lib/puppet/jruby-gems' 158 | } 159 | } 160 | $root_group = undef 161 | $puppetconf_mode = '0644' 162 | } 163 | } 164 | 165 | $http_connect_timeout = undef 166 | $http_read_timeout = undef 167 | 168 | $autosign = "${dir}/autosign.conf" 169 | $autosign_entries = [] 170 | $autosign_mode = '0664' 171 | $autosign_content = undef 172 | $autosign_source = undef 173 | 174 | $puppet_cmd = "${bindir}/puppet" 175 | $puppetserver_cmd = "${bindir}/puppetserver" 176 | 177 | $manage_packages = true 178 | 179 | if $facts['os']['family'] == 'Windows' { 180 | $dir_owner = undef 181 | $dir_group = undef 182 | } else { 183 | $dir_owner = 'root' 184 | $dir_group = $root_group 185 | } 186 | 187 | $package_provider = $facts['os']['family'] ? { 188 | 'windows' => 'chocolatey', 189 | default => undef, 190 | } 191 | 192 | $package_source = undef 193 | $package_install_options = undef 194 | 195 | # Allow any to the CRL. Needed in case of puppet CA proxy 196 | $allow_any_crl_auth = false 197 | 198 | # Authenticated nodes to allow 199 | $auth_allowed = ['$1'] 200 | 201 | # Will this host be a puppet agent ? 202 | $agent = true 203 | $client_certname = $trusted['certname'] 204 | 205 | # Set by the Foreman ENC 206 | $agent_server_hostname = getvar('puppetmaster') 207 | 208 | # Hashes containing additional settings 209 | $additional_settings = {} 210 | $agent_additional_settings = {} 211 | $server_additional_settings = {} 212 | 213 | # Will this host be a puppetserver? 214 | $server = false 215 | $server_ca = true 216 | $server_ca_crl_sync = false 217 | $server_reports = 'foreman' 218 | $server_external_nodes = "${dir}/node.rb" 219 | $server_trusted_external_command = undef 220 | $server_request_timeout = 60 221 | $server_certname = $trusted['certname'] 222 | $server_strict_variables = false 223 | $server_http = false 224 | $server_http_port = 8139 225 | 226 | # Need a new server template for the server? 227 | $server_template = 'puppet/server/puppet.conf.erb' 228 | # Template for server settings in [main] 229 | $server_main_template = 'puppet/server/puppet.conf.main.erb' 230 | 231 | # Set 'false' for static environments, or 'true' for git-based workflow 232 | $server_git_repo = false 233 | # Git branch to puppet env mapping for the post receive hook 234 | $server_git_branch_map = {} 235 | 236 | # Owner of the environments dir: for cases external service needs write 237 | # access to manage it. 238 | $server_environments_owner = $user 239 | $server_environments_group = $root_group 240 | $server_environments_mode = '0755' 241 | $server_environments_recurse = false 242 | # Where we store our puppet environments 243 | $server_envs_dir = ["${codedir}/environments"] 244 | $server_envs_target = undef 245 | # Modules in this directory would be shared across all environments 246 | $server_common_modules_path = unique(["${server_envs_dir[0]}/common", "${codedir}/modules", "${sharedir}/modules", '/usr/share/puppet/modules']) 247 | 248 | # Dynamic environments config, ignore if the git_repo is 'false' 249 | # Path to the repository on disk 250 | $server_git_repo_path = "${vardir}/puppet.git" 251 | # Umask for vcsrepo operations 252 | $server_git_repo_umask = '0022' 253 | # mode of the repository hooks 254 | $server_git_repo_hook_mode = '0755' 255 | # user of the repository 256 | $server_git_repo_user = $user 257 | # group of the repository 258 | $server_git_repo_group = $user 259 | # Override these if you need your own hooks 260 | $server_post_hook_content = 'puppet/server/post-receive.erb' 261 | $server_post_hook_name = 'post-receive' 262 | $server_custom_trusted_oid_mapping = undef 263 | 264 | $server_storeconfigs = false 265 | 266 | $puppet_major = regsubst($facts['puppetversion'], '^(\d+)\..*$', '\1') 267 | 268 | # Add support for OpenVox. Default to puppet if nothing is installed yet 269 | $puppet_implementation = pick($facts['implementation'], 'puppet') 270 | $puppetserver_implementation = bool2str($puppet_implementation == 'openvox', 'openvox-server', 'puppetserver') 271 | 272 | $server_package = if ($facts['os']['family'] =~ /(FreeBSD|DragonFly)/) { 273 | "${puppetserver_implementation}${puppet_major}" 274 | } else { 275 | $puppetserver_implementation 276 | } 277 | 278 | $server_ssl_dir = $ssldir 279 | $server_version = undef 280 | 281 | $client_package = if $aio_package or ($facts['os']['name'] == 'Debian' and versioncmp($facts['os']['release']['major'], '12') >= 0) { 282 | ["${puppet_implementation}-agent"] 283 | } elsif ($facts['os']['family'] =~ /(FreeBSD|DragonFly)/) { 284 | ["${puppet_implementation}${puppet_major}"] 285 | } else { 286 | [$puppet_implementation] 287 | } 288 | 289 | # Puppet service name 290 | $service_name = 'puppet' 291 | 292 | # Puppet oneshot systemd service and timer name 293 | $systemd_unit_name = 'puppet-run' 294 | # Mechanisms to manage and reload/restart the agent 295 | # If supported on the OS, reloading is prefered since it does not kill a currently active puppet run 296 | if $facts['service_provider'] == 'systemd' { 297 | $agent_restart_command = "/bin/systemctl reload-or-restart ${service_name}" 298 | $unavailable_runmodes = $facts['os']['family'] ? { 299 | 'Archlinux' => ['cron'], 300 | default => [], 301 | } 302 | } else { 303 | case $facts['os']['family'] { 304 | 'Windows': { 305 | $agent_restart_command = undef 306 | $unavailable_runmodes = ['cron', 'systemd.timer'] 307 | } 308 | default : { 309 | $agent_restart_command = undef 310 | $unavailable_runmodes = ['systemd.timer'] 311 | } 312 | } 313 | } 314 | 315 | # Foreman parameters 316 | $lower_fqdn = downcase($facts['networking']['fqdn']) 317 | $server_foreman = true 318 | $server_foreman_facts = true 319 | $server_puppet_basedir = $aio_package ? { 320 | true => '/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet', 321 | false => undef, 322 | } 323 | $server_foreman_url = undef 324 | $server_foreman_ssl_ca = undef 325 | $server_foreman_ssl_cert = undef 326 | $server_foreman_ssl_key = undef 327 | 328 | # Which Parser do we want to use? https://docs.puppetlabs.com/references/latest/configuration.html#parser 329 | $server_parser = 'current' 330 | 331 | # Timeout for cached environments, changed in puppet 3.7.x 332 | $server_environment_timeout = undef 333 | 334 | # puppet server configuration file 335 | $server_jvm_config = $facts['os']['family'] ? { 336 | 'RedHat' => '/etc/sysconfig/puppetserver', 337 | 'Debian' => '/etc/default/puppetserver', 338 | default => '/etc/default/puppetserver', 339 | } 340 | 341 | $server_jvm_extra_args = undef 342 | $server_jvm_cli_args = undef 343 | 344 | # This is some very trivial "tuning". See the puppet reference: 345 | # https://docs.puppet.com/puppetserver/latest/tuning_guide.html 346 | $mem_in_mb = $facts['memory']['system']['total_bytes'] / 1024 / 1024 347 | if $mem_in_mb >= 3072 { 348 | $server_jvm_min_heap_size = '2G' 349 | $server_jvm_max_heap_size = '2G' 350 | $server_max_active_instances = min(abs($facts['processors']['count']), 4) 351 | } elsif $mem_in_mb >= 1024 { 352 | $server_max_active_instances = 1 353 | $server_jvm_min_heap_size = '1G' 354 | $server_jvm_max_heap_size = '1G' 355 | } else { 356 | # VMs with 1GB RAM and a crash kernel enabled usually have an effective 992MB RAM 357 | $server_max_active_instances = 1 358 | $server_jvm_min_heap_size = '768m' 359 | $server_jvm_max_heap_size = '768m' 360 | } 361 | 362 | $server_ssl_dir_manage = true 363 | $server_ssl_key_manage = true 364 | $server_default_manifest = false 365 | $server_default_manifest_path = '/etc/puppet/manifests/default_manifest.pp' 366 | $server_default_manifest_content = '' # lint:ignore:empty_string_assignment 367 | $server_max_requests_per_instance = 0 368 | $server_max_queued_requests = 0 369 | $server_max_retry_delay = 1800 370 | $server_multithreaded = false 371 | $server_idle_timeout = 1200000 372 | $server_web_idle_timeout = 30000 373 | $server_connect_timeout = 120000 374 | $server_ca_auth_required = true 375 | $server_ca_client_self_delete = false 376 | $server_admin_api_allowlist = ['localhost', $lower_fqdn] 377 | $server_ca_client_allowlist = ['localhost', $lower_fqdn] 378 | $server_cipher_suites = [ 379 | 'TLS_AES_128_GCM_SHA256', 380 | 'TLS_AES_256_GCM_SHA384', 381 | 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256', 382 | 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384', 383 | 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256', 384 | 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384', 385 | 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256', 386 | 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384', 387 | ] 388 | $server_ssl_protocols = ['TLSv1.3', 'TLSv1.2'] 389 | $server_ssl_chain_filepath = undef 390 | $server_check_for_updates = true 391 | $server_environment_class_cache_enabled = false 392 | $server_allow_header_cert_info = false 393 | $server_ca_allow_sans = false 394 | $server_ca_allow_auth_extensions = false 395 | $server_ca_enable_infra_crl = false 396 | $server_ca_allow_auto_renewal = false 397 | $server_ca_allow_auto_renewal_cert_ttl = '60d' 398 | $server_max_open_files = undef 399 | $server_environment_vars = {} 400 | 401 | $server_puppetserver_version = undef 402 | 403 | # Puppetserver metrics shipping 404 | $server_metrics_jmx_enable = true 405 | $server_metrics_graphite_enable = false 406 | $server_metrics_graphite_host = '127.0.0.1' 407 | $server_metrics_graphite_port = 2003 408 | $server_metrics_server_id = $lower_fqdn 409 | $server_metrics_graphite_interval = 5 410 | $server_metrics_allowed = undef 411 | 412 | # Should the /puppet/experimental route be enabled? 413 | $server_puppetserver_experimental = true 414 | 415 | # For custom auth.conf settings allow passing in a template 416 | $server_puppetserver_auth_template = undef 417 | 418 | # Normally agents can only fetch their own catalogs. If you want some nodes to be able to fetch *any* catalog, add them here. 419 | $server_puppetserver_trusted_agents = [] 420 | $server_puppetserver_trusted_certificate_extensions = [] 421 | } 422 | -------------------------------------------------------------------------------- /spec/classes/puppet_agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet' do 4 | on_supported_os.each do |os, facts| 5 | context "on #{os}" do 6 | case facts[:os]['family'] 7 | when 'FreeBSD' 8 | puppet_major = facts[:puppetversion].to_i 9 | 10 | bindir = '/usr/local/bin' 11 | client_package = "puppet#{puppet_major}" 12 | confdir = '/usr/local/etc/puppet' 13 | package_provider = nil 14 | when 'windows' 15 | bindir = 'C:/ProgramData/PuppetLabs/puppet/bin' 16 | client_package = 'puppet-agent' 17 | confdir = 'C:/ProgramData/PuppetLabs/puppet/etc' 18 | package_provider = 'chocolatey' 19 | when 'Archlinux' 20 | bindir = '/usr/bin' 21 | client_package = 'puppet' 22 | confdir = '/etc/puppetlabs/puppet' 23 | package_provider = nil 24 | else 25 | bindir = '/opt/puppetlabs/bin' 26 | client_package = 'puppet-agent' 27 | confdir = '/etc/puppetlabs/puppet' 28 | package_provider = nil 29 | end 30 | 31 | let(:facts) do 32 | # Cron/systemd timers are based on the IP - make it consistent 33 | override_facts(facts, networking: {ip: '192.0.2.100'}) 34 | end 35 | 36 | let :params do 37 | { 38 | agent: true 39 | } 40 | end 41 | 42 | describe 'with no custom parameters' do 43 | # For windows we specify a package provider which doesn't compile 44 | if facts[:os]['family'] != 'windows' 45 | it { is_expected.to compile.with_all_deps } 46 | end 47 | 48 | # install 49 | it do 50 | is_expected.to contain_class('puppet::agent::install') 51 | .with_manage_packages(true) 52 | .with_package_name([client_package]) 53 | .with_package_version('present') 54 | .with_package_provider(package_provider) 55 | .with_package_source(nil) 56 | .that_notifies(['Class[puppet::agent::config]', 'Class[puppet::agent::service]']) 57 | end 58 | 59 | it do 60 | is_expected.to contain_package(client_package) 61 | .with_ensure('present') 62 | .with_provider(package_provider) 63 | .with_source(nil) 64 | .with_install_options(nil) 65 | end 66 | 67 | # config 68 | it { is_expected.to contain_class('puppet::agent::config').that_notifies('Class[puppet::agent::service]') } 69 | it { is_expected.to contain_file(confdir).with_ensure('directory') } 70 | it { is_expected.to contain_concat("#{confdir}/puppet.conf") } 71 | it { is_expected.to contain_concat__fragment('puppet.conf_agent').with_content(/^\[agent\]/) } 72 | it { is_expected.to contain_puppet__config__agent('report').with_value('true') } 73 | it { is_expected.not_to contain_puppet__config__agent('prerun_command') } 74 | it { is_expected.not_to contain_puppet__config__agent('postrun_command') } 75 | 76 | # service 77 | it { is_expected.to contain_class('puppet::agent::service') } 78 | 79 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(true) } 80 | it do 81 | is_expected.to contain_service('puppet') 82 | .with_ensure('running') 83 | .with_name('puppet') 84 | .with_hasstatus('true') 85 | .with_enable('true') 86 | end 87 | 88 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } 89 | if os =~ /\A(windows|archlinux)/ 90 | it { is_expected.not_to contain_cron('puppet') } 91 | else 92 | it { is_expected.to contain_cron('puppet').with_ensure('absent') } 93 | end 94 | 95 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 96 | case os 97 | when /\A(debian|redhat|centos|scientific|fedora|ubuntu|sles|archlinux|oraclelinux|almalinux|rocky)-/ 98 | it do 99 | is_expected.to contain_service('puppet-run.timer') 100 | .with_ensure(false) 101 | .with_provider('systemd') 102 | .with_name('puppet-run.timer') 103 | .with_enable(false) 104 | end 105 | 106 | it { is_expected.to contain_file('/etc/systemd/system/puppet-run.timer').with_ensure(:absent) } 107 | it { is_expected.to contain_file('/etc/systemd/system/puppet-run.service').with_ensure(:absent) } 108 | else 109 | it { is_expected.not_to contain_service('puppet-run.timer') } 110 | it { is_expected.not_to contain_file('/etc/systemd/system/puppet-run.timer') } 111 | it { is_expected.not_to contain_file('/etc/systemd/system/puppet-run.service') } 112 | end 113 | end 114 | 115 | describe 'set prerun_command will be included in config' do 116 | let :params do 117 | super().merge(prerun_command: '/my/prerun') 118 | end 119 | 120 | it { is_expected.to contain_puppet__config__agent('prerun_command').with_value('/my/prerun') } 121 | end 122 | 123 | describe 'set postrun_command will be included in config' do 124 | let :params do 125 | super().merge(postrun_command: '/my/postrun') 126 | end 127 | 128 | it { is_expected.to contain_puppet__config__agent('postrun_command').with_value('/my/postrun') } 129 | end 130 | 131 | describe 'with additional settings' do 132 | let :params do 133 | super().merge(agent_additional_settings: { 'ignoreschedules' => true }) 134 | end 135 | 136 | it { is_expected.to contain_puppet__config__agent('ignoreschedules').with_value('true') } 137 | end 138 | 139 | context 'manage_packages' do 140 | describe 'when manage_packages => false' do 141 | let :params do 142 | super().merge(manage_packages: false) 143 | end 144 | 145 | it { is_expected.not_to contain_package(client_package) } 146 | end 147 | 148 | describe "when manage_packages => 'agent'" do 149 | let :params do 150 | super().merge(manage_packages: 'agent') 151 | end 152 | 153 | it { is_expected.to contain_package(client_package) } 154 | end 155 | 156 | describe "when manage_packages => 'server'" do 157 | let :params do 158 | super().merge(manage_packages: 'server') 159 | end 160 | 161 | it { is_expected.not_to contain_package(client_package) } 162 | end 163 | end 164 | 165 | context 'runmode' do 166 | describe 'when runmode => cron' do 167 | let :params do 168 | super().merge(runmode: 'cron') 169 | end 170 | 171 | case os 172 | when /\A(windows|archlinux)/ 173 | it { is_expected.to raise_error(Puppet::Error, /Runmode of cron not supported on #{facts[:kernel]} operating systems!/) } 174 | when /\A(debian|redhat|centos|scientific|fedora|ubuntu|sles|oraclelinux|almalinux|rocky)-/ 175 | it { is_expected.to compile.with_all_deps } 176 | it { is_expected.to contain_concat__fragment('puppet.conf_agent') } 177 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(true) } 178 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 179 | it do 180 | is_expected.to contain_service('puppet') 181 | .with_ensure('stopped') 182 | .with_name('puppet') 183 | .with_hasstatus('true') 184 | .with_enable('false') 185 | end 186 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 187 | it { is_expected.to contain_service('puppet-run.timer').with_ensure(false) } 188 | it do 189 | is_expected.to contain_cron('puppet') 190 | .with_command("#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize") 191 | .with_user('root') 192 | .with_minute(%w[10 40]) 193 | .with_hour('*') 194 | end 195 | else 196 | it { is_expected.to compile.with_all_deps } 197 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(true) } 198 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 199 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 200 | it { is_expected.not_to contain_service('puppet-run.timer') } 201 | it do 202 | is_expected.to contain_cron('puppet') 203 | .with_command("#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize") 204 | .with_user('root') 205 | .with_minute(%w[10 40]) 206 | .with_hour('*') 207 | end 208 | end 209 | end 210 | 211 | describe 'when runmode => cron with specified time' do 212 | let :params do 213 | super().merge(runmode: 'cron', 214 | run_hour: 22, 215 | run_minute: 01 216 | ) 217 | end 218 | 219 | case os 220 | when /\A(windows|archlinux)/ 221 | it { is_expected.to raise_error(Puppet::Error, /Runmode of cron not supported on #{facts[:kernel]} operating systems!/) } 222 | when /\A(debian|redhat|centos|scientific|fedora|ubuntu|sles|oraclelinux|almalinux|rocky)-/ 223 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(true) } 224 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 225 | it do 226 | is_expected.to contain_service('puppet') 227 | .with_ensure('stopped') 228 | .with_name('puppet') 229 | .with_hasstatus('true') 230 | .with_enable('false') 231 | end 232 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 233 | it { is_expected.to contain_service('puppet-run.timer').with_ensure(false) } 234 | it do 235 | is_expected.to contain_cron('puppet') 236 | .with_command("#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize") 237 | .with_user('root') 238 | .with_minute('1') 239 | .with_hour('22') 240 | end 241 | else 242 | it { is_expected.to compile.with_all_deps } 243 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(true) } 244 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 245 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 246 | it { is_expected.not_to contain_service('puppet-run.timer') } 247 | it do 248 | is_expected.to contain_cron('puppet') 249 | .with_command("#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize") 250 | .with_user('root') 251 | .with_minute('1') 252 | .with_hour('22') 253 | end 254 | end 255 | end 256 | 257 | describe 'when runmode => systemd.timer' do 258 | let :params do 259 | super().merge(runmode: 'systemd.timer') 260 | end 261 | 262 | case os 263 | when /\A(debian|redhat|centos|scientific|fedora|ubuntu|sles|archlinux|oraclelinux|almalinux|rocky)-/ 264 | it { is_expected.to compile.with_all_deps } 265 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 266 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } 267 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(true) } 268 | it { is_expected.to contain_service('puppet-run.timer').with_ensure(true) } 269 | 270 | it do 271 | is_expected.to contain_file('/etc/systemd/system/puppet-run.timer') 272 | .with_content(/.*OnCalendar\=\*-\*-\* \*\:10,40:00.*/) 273 | end 274 | 275 | it do 276 | is_expected.to contain_file('/etc/systemd/system/puppet-run.timer') 277 | .with_content(/^RandomizedDelaySec\=0$/) 278 | end 279 | 280 | it do 281 | is_expected.to contain_file('/etc/systemd/system/puppet-run.service') 282 | .with_content(%r{^ExecStart=#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize --detailed-exitcode --no-usecacheonfailure$}) 283 | end 284 | 285 | it do 286 | is_expected.to contain_service('puppet-run.timer') 287 | .with_provider('systemd') 288 | .with_ensure(true) 289 | .with_name('puppet-run.timer') 290 | .with_enable(true) 291 | end 292 | else 293 | it { is_expected.to raise_error(Puppet::Error, /Runmode of systemd.timer not supported on #{facts[:kernel]} operating systems!/) } 294 | end 295 | end 296 | 297 | describe 'when runmode => systemd.timer with configured time' do 298 | let :params do 299 | super().merge(runmode: 'systemd.timer', 300 | run_hour: 22, 301 | run_minute: 01 302 | ) 303 | end 304 | 305 | case os 306 | when /\A(debian|redhat|centos|scientific|fedora|ubuntu|sles|archlinux|oraclelinux|almalinux|rocky)-/ 307 | it { is_expected.to compile.with_all_deps } 308 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 309 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } 310 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(true) } 311 | it { is_expected.to contain_service('puppet-run.timer').with_ensure(true) } 312 | 313 | it do 314 | is_expected.to contain_file('/etc/systemd/system/puppet-run.timer') 315 | .with_content(/.*OnCalendar\=\*-\*-\* 22:1:00.*/) 316 | end 317 | 318 | it do 319 | is_expected.to contain_file('/etc/systemd/system/puppet-run.timer') 320 | .with_content(/^RandomizedDelaySec\=0$/) 321 | end 322 | 323 | it do 324 | is_expected.to contain_file('/etc/systemd/system/puppet-run.service') 325 | .with_content(%r{^ExecStart=#{bindir}/puppet agent --config #{confdir}/puppet.conf --onetime --no-daemonize --detailed-exitcode --no-usecacheonfailure$}) 326 | end 327 | 328 | it do 329 | is_expected.to contain_service('puppet-run.timer') 330 | .with_provider('systemd') 331 | .with_ensure(true) 332 | .with_name('puppet-run.timer') 333 | .with_enable(true) 334 | end 335 | else 336 | it { is_expected.to raise_error(Puppet::Error, /Runmode of systemd.timer not supported on #{facts[:kernel]} operating systems!/) } 337 | end 338 | end 339 | 340 | describe 'when runmode => none' do 341 | let :params do 342 | super().merge(runmode: 'none') 343 | end 344 | 345 | # For windows we specify a package provider which doesn't compile 346 | if facts[:os]['family'] != 'windows' 347 | it { is_expected.to compile.with_all_deps } 348 | end 349 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 350 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } 351 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 352 | 353 | case os 354 | when /\A(debian|redhat|centos|scientific|fedora|ubuntu|sles|archlinux|oraclelinux|almalinux|rocky)-/ 355 | it { is_expected.to contain_service('puppet-run.timer').with_ensure(false) } 356 | else 357 | it { is_expected.not_to contain_service('puppet-run.timer') } 358 | end 359 | end 360 | 361 | describe 'when runmode => unmanaged' do 362 | let :params do 363 | super().merge(runmode: 'unmanaged') 364 | end 365 | 366 | # For windows we specify a package provider which doesn't compile 367 | if facts[:os]['family'] != 'windows' 368 | it { is_expected.to compile.with_all_deps } 369 | end 370 | it { is_expected.to contain_class('puppet::agent::service::daemon').with_enabled(false) } 371 | it { is_expected.to contain_class('puppet::agent::service::cron').with_enabled(false) } 372 | it { is_expected.to contain_class('puppet::agent::service::systemd').with_enabled(false) } 373 | it { is_expected.not_to contain_cron('puppet') } 374 | it { is_expected.not_to contain_service('puppet') } 375 | it { is_expected.not_to contain_service('puppet-run.timer') } 376 | end 377 | end 378 | 379 | describe 'when unavailable_runmodes => ["cron"]' do 380 | let :params do 381 | super().merge(unavailable_runmodes: ['cron']) 382 | end 383 | 384 | it { is_expected.not_to contain_cron('puppet') } 385 | end 386 | 387 | describe 'with custom service_name' do 388 | let :params do 389 | super().merge(service_name: 'pe-puppet') 390 | end 391 | 392 | it { is_expected.to contain_service('puppet').with_name('pe-puppet') } 393 | end 394 | 395 | context 'with report => false' do 396 | let :params do 397 | super().merge(report: false) 398 | end 399 | 400 | it { is_expected.to contain_puppet__config__agent('report').with_value('false') } 401 | end 402 | 403 | context 'with agent_manage_environment false' do 404 | let(:params) { { agent_manage_environment: false } } 405 | 406 | it do 407 | is_expected.not_to contain_puppet__config__agent('environment') 408 | end 409 | end 410 | end 411 | end 412 | end 413 | -------------------------------------------------------------------------------- /spec/classes/puppet_server_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'puppet' do 4 | on_supported_os.each do |os, facts| 5 | context "on #{os}", unless: unsupported_puppetserver_osfamily(facts[:os]['family']) do 6 | if facts[:os]['family'] == 'FreeBSD' 7 | codedir = '/usr/local/etc/puppet' 8 | confdir = '/usr/local/etc/puppet' 9 | etcdir = '/usr/local/etc/puppet' 10 | puppetserver_etcdir = '/usr/local/etc/puppetserver' 11 | puppetserver_logdir = '/var/log/puppetserver' 12 | puppetserver_rundir = '/var/run/puppetserver' 13 | puppetserver_vardir = '/var/puppet/server/data/puppetserver' 14 | sharedir = '/usr/local/share/puppet' 15 | ssldir = '/var/puppet/ssl' 16 | vardir = '/var/puppet' 17 | rubydir = %r{^/usr/local/lib/ruby/site_ruby/\d+\.\d+/puppet$} 18 | puppetserver_pkg = "puppetserver#{facts[:puppetversion].to_i}" 19 | puppetcacmd = '/usr/local/bin/puppetserver ca setup' 20 | else 21 | codedir = '/etc/puppetlabs/code' 22 | confdir = '/etc/puppetlabs/puppet' 23 | etcdir = '/etc/puppetlabs/puppet' 24 | puppetserver_etcdir = '/etc/puppetlabs/puppetserver' 25 | puppetserver_logdir = '/var/log/puppetlabs/puppetserver' 26 | puppetserver_rundir = '/var/run/puppetlabs/puppetserver' 27 | puppetserver_vardir = '/opt/puppetlabs/server/data/puppetserver' 28 | sharedir = '/opt/puppetlabs/puppet' 29 | ssldir = '/etc/puppetlabs/puppet/ssl' 30 | vardir = '/opt/puppetlabs/puppet/cache' 31 | rubydir = '/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet' 32 | puppetserver_pkg = 'puppetserver' 33 | puppetcacmd = '/opt/puppetlabs/bin/puppetserver ca setup' 34 | end 35 | conf_file = "#{confdir}/puppet.conf" 36 | conf_d_dir = "#{puppetserver_etcdir}/conf.d" 37 | environments_dir = "#{codedir}/environments" 38 | cadir = "#{puppetserver_etcdir}/ca" 39 | 40 | let(:facts) { facts } 41 | 42 | let(:params) do 43 | { 44 | server: true, 45 | server_certname: 'puppetserver.example.com' 46 | } 47 | end 48 | 49 | describe 'with no custom parameters' do 50 | it { should compile.with_all_deps } 51 | 52 | # install 53 | it { should contain_class('puppet::server::install') } 54 | it { should contain_user('puppet') } 55 | it { should contain_package(puppetserver_pkg).with_install_options(nil) } 56 | 57 | # config 58 | it { should contain_class('puppet::server::config') } 59 | it { should contain_puppet__config__main('reports').with_value('foreman') } 60 | it { should contain_puppet__config__main('hiera_config').with_value('$confdir/hiera.yaml') } 61 | it { should contain_puppet__config__main('environmentpath').with_value(environments_dir) } 62 | it do 63 | should contain_puppet__config__main('basemodulepath') 64 | .with_value(["#{environments_dir}/common", "#{codedir}/modules", "#{sharedir}/modules", '/usr/share/puppet/modules']) 65 | .with_joiner(':') 66 | end 67 | it { should_not contain_puppet__config__main('default_manifest') } 68 | it { should contain_puppet__config__server('autosign').with_value("#{etcdir}\/autosign.conf \{ mode = 0664 \}") } 69 | it { should contain_puppet__config__server('ca').with_value('true') } 70 | it { should contain_puppet__config__server('certname').with_value('puppetserver.example.com') } 71 | it { should contain_puppet__config__server('parser').with_value('current') } 72 | it { should contain_puppet__config__server('strict_variables').with_value('false') } 73 | it { should contain_puppet__config__server('ssldir').with_value(ssldir) } 74 | it { should contain_puppet__config__server('storeconfigs').with_value(false) } 75 | it { should_not contain_puppet__config__server('environment_timeout') } 76 | it { should_not contain_puppet__config__server('manifest') } 77 | it { should_not contain_puppet__config__server('modulepath') } 78 | it { should_not contain_puppet__config__server('trusted_external_command') } 79 | 80 | it { should contain_puppet__config__server('external_nodes').with_value("#{etcdir}\/node.rb") } 81 | it { should contain_puppet__config__server('node_terminus').with_value('exec') } 82 | it { should contain_puppet__config__server('logdir').with_value(puppetserver_logdir) } 83 | it { should contain_puppet__config__server('rundir').with_value(puppetserver_rundir) } 84 | it { should contain_puppet__config__server('vardir').with_value(puppetserver_vardir) } 85 | 86 | it 'should set up SSL permissions' do 87 | should contain_file("#{ssldir}/private_keys") \ 88 | .with_group('puppet') \ 89 | .with_mode('0750') 90 | 91 | should contain_file("#{ssldir}/private_keys/puppetserver.example.com.pem") \ 92 | .with_group('puppet') \ 93 | .with_mode('0640') 94 | 95 | should contain_exec('puppet_server_config-create_ssl_dir') \ 96 | .with_creates(ssldir) \ 97 | .with_command("/bin/mkdir -p #{ssldir}") \ 98 | .with_umask('0022') 99 | 100 | should contain_exec('puppet_server_config-generate_ca_cert') \ 101 | .with_creates("#{cadir}/ca_crt.pem") \ 102 | .with_command(puppetcacmd) \ 103 | .with_umask('0022') \ 104 | .that_requires(["Concat[#{conf_file}]", 'Exec[puppet_server_config-create_ssl_dir]']) 105 | end 106 | 107 | it { should contain_exec('puppet_server_config-generate_ca_cert').that_notifies('Service[puppetserver]') } 108 | 109 | it 'should set up the environments' do 110 | should contain_file(environments_dir) 111 | .with_ensure('directory') 112 | .with_owner('puppet') 113 | .with_group(nil) 114 | .with_recurse(false) 115 | .with_mode('0755') 116 | 117 | should contain_file(sharedir).with_ensure('directory') 118 | 119 | should contain_file("#{codedir}/environments/common") 120 | .with_ensure('directory') 121 | .with_owner('puppet') 122 | .with_group(nil) 123 | .with_mode('0755') 124 | 125 | should contain_file("#{sharedir}/modules") 126 | .with_ensure('directory') 127 | .with_owner('puppet') 128 | .with_group(nil) 129 | .with_mode('0755') 130 | end 131 | 132 | it { should contain_concat(conf_file) } 133 | 134 | it { should_not contain_puppet__config__agent('http_connect_timeout') } 135 | it { should_not contain_puppet__config__agent('http_read_timeout') } 136 | it { should_not contain_file("#{confdir}/custom_trusted_oid_mapping.yaml") } 137 | 138 | it { should contain_file("#{confdir}/autosign.conf") } 139 | it { should_not contain_file("#{confdir}/autosign.conf").with_content(/# Managed by Puppet/) } 140 | it { should_not contain_file("#{confdir}/autosign.conf").with_content(/foo.bar/) } 141 | 142 | it 'should set up the ENC' do 143 | should contain_class('puppetserver_foreman') 144 | .with_foreman_url('https://foo.example.com') 145 | .with_enc_upload_facts(true) 146 | .with_enc_timeout(60) 147 | .with_puppet_home(puppetserver_vardir) 148 | .with_puppet_etcdir(etcdir) 149 | .with_puppet_basedir(rubydir) 150 | end 151 | 152 | # service 153 | it { should contain_class('puppet::server::service') } 154 | it { should contain_class('puppet::server::puppetserver') } 155 | end 156 | 157 | describe 'with server_foreman_url' do 158 | let(:params) { super().merge(server_foreman_url: 'https://foreman.example.com') } 159 | 160 | it { should compile.with_all_deps } 161 | it { should contain_class('puppet').with_server_foreman_url('https://foreman.example.com') } 162 | end 163 | 164 | describe 'with ip parameter' do 165 | let(:params) do 166 | super().merge(server_ip: '127.0.0.1') 167 | end 168 | 169 | it { should compile.with_all_deps } 170 | it { should contain_class('puppet::server').with_ip('127.0.0.1') } 171 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(/host: 127.0.0.1/) } 172 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(/ssl-host: 127.0.0.1/) } 173 | end 174 | 175 | context 'manage_packages' do 176 | tests = { 177 | false => false, 178 | 'agent' => false, 179 | 'server' => true 180 | } 181 | 182 | tests.each do |value, expected| 183 | describe "when manage_packages => #{value.inspect}" do 184 | let(:params) do 185 | super().merge(manage_packages: value) 186 | end 187 | 188 | it { should compile.with_all_deps } 189 | if expected 190 | it { should contain_package(puppetserver_pkg) } 191 | else 192 | it { should_not contain_package(puppetserver_pkg) } 193 | end 194 | end 195 | end 196 | end 197 | 198 | describe 'when autosign => true' do 199 | let(:params) do 200 | super().merge(autosign: true) 201 | end 202 | 203 | it { should contain_puppet__config__server('autosign').with_value(true) } 204 | end 205 | 206 | describe 'when autosign => /somedir/custom_autosign, autosign_mode => 664' do 207 | let(:params) do 208 | super().merge( 209 | autosign: '/somedir/custom_autosign', 210 | autosign_mode: '664' 211 | ) 212 | end 213 | 214 | it { should contain_puppet__config__server('autosign').with_value('/somedir/custom_autosign { mode = 664 }') } 215 | end 216 | 217 | describe "when autosign_entries set to ['foo.bar']" do 218 | let(:params) do 219 | super().merge(autosign_entries: ['foo.bar']) 220 | end 221 | 222 | it 'should contain autosign.conf with content set' do 223 | should contain_file("#{confdir}/autosign.conf") 224 | should contain_file("#{confdir}/autosign.conf").with_content(/# Managed by Puppet/) 225 | should contain_file("#{confdir}/autosign.conf").with_content(/foo.bar/) 226 | end 227 | end 228 | 229 | describe "when autosign_content => set to foo.bar and and autosign_entries set to ['foo.bar']=> true" do 230 | let(:params) do 231 | super().merge( 232 | autosign_content: 'foo.bar', 233 | autosign_entries: ['foo.bar'] 234 | ) 235 | end 236 | 237 | it { should raise_error(Puppet::Error, %r{Cannot set both autosign_content/autosign_source and autosign_entries}) } 238 | end 239 | 240 | describe "when autosign_source => set to puppet:///foo/bar and and autosign_entries set to ['foo.bar']=> true" do 241 | let(:params) do 242 | super().merge( 243 | autosign_source: 'puppet:///foo/bar', 244 | autosign_entries: ['foo.bar'] 245 | ) 246 | end 247 | 248 | it { should raise_error(Puppet::Error, %r{Cannot set both autosign_content\/autosign_source and autosign_entries}) } 249 | end 250 | 251 | context 'when autosign => /usr/local/bin/custom_autosign.sh, autosign_mode => 775' do 252 | let(:params) do 253 | super().merge( 254 | autosign: '/usr/local/bin/custom_autosign.sh', 255 | autosign_mode: '775' 256 | ) 257 | end 258 | 259 | describe "when autosign_content set to 'foo.bar'" do 260 | let(:params) do 261 | super().merge(autosign_content: 'foo.bar') 262 | end 263 | 264 | it { should contain_puppet__config__server('autosign').with_value('/usr/local/bin/custom_autosign.sh { mode = 775 }') } 265 | it { should contain_file('/usr/local/bin/custom_autosign.sh').with_content('foo.bar') } 266 | end 267 | 268 | describe "autosign_source set to 'puppet:///foo/bar'" do 269 | let(:params) do 270 | super().merge(autosign_source: 'puppet:///foo/bar') 271 | end 272 | 273 | it { should contain_puppet__config__server('autosign').with_value('/usr/local/bin/custom_autosign.sh { mode = 775 }') } 274 | it { should contain_file('/usr/local/bin/custom_autosign.sh').with_source('puppet:///foo/bar') } 275 | end 276 | end 277 | 278 | describe "when hiera_config => '/etc/puppet/hiera/production/hiera.yaml'" do 279 | let(:params) do 280 | super().merge(hiera_config: '/etc/puppet/hiera/production/hiera.yaml') 281 | end 282 | 283 | it { should contain_puppet__config__main('hiera_config').with_value('/etc/puppet/hiera/production/hiera.yaml') } 284 | end 285 | 286 | describe 'without foreman' do 287 | let(:params) do 288 | super().merge( 289 | server_foreman: false, 290 | server_reports: 'store', 291 | server_external_nodes: '' 292 | ) 293 | end 294 | 295 | it { should_not contain_class('puppetserver_foreman') } 296 | it { should_not contain_puppet__config__server('node_terminus') } 297 | it { should_not contain_puppet__config__server('external_nodes') } 298 | end 299 | 300 | describe 'with server_default_manifest => true and undef content' do 301 | let(:params) do 302 | super().merge(server_default_manifest: true) 303 | end 304 | 305 | it { should contain_puppet__config__main('default_manifest').with_value('/etc/puppet/manifests/default_manifest.pp') } 306 | it { should_not contain_file('/etc/puppet/manifests/default_manifest.pp') } 307 | end 308 | 309 | describe 'with server_default_manifest => true and server_default_manifest_content => "include foo"' do 310 | let(:params) do 311 | super().merge( 312 | server_default_manifest: true, 313 | server_default_manifest_content: 'include foo' 314 | ) 315 | end 316 | 317 | it { should contain_puppet__config__main('default_manifest').with_value('/etc/puppet/manifests/default_manifest.pp') } 318 | it { should contain_file('/etc/puppet/manifests/default_manifest.pp').with_content('include foo') } 319 | end 320 | 321 | describe 'with git repo' do 322 | let(:params) do 323 | super().merge(server_git_repo: true) 324 | end 325 | 326 | it { is_expected.to compile.with_all_deps } 327 | 328 | it do 329 | should contain_class('puppet::server') 330 | .with_git_repo(true) 331 | .with_git_repo_path("#{vardir}/puppet.git") 332 | .with_post_hook_name('post-receive') 333 | end 334 | 335 | it 'should set up the environments directory' do 336 | should contain_file(environments_dir) \ 337 | .with_ensure('directory') \ 338 | .with_owner('puppet') 339 | end 340 | 341 | it 'should create the puppet user' do 342 | shell = case facts[:os]['family'] 343 | when /^(FreeBSD|DragonFly)$/ 344 | '/usr/local/bin/git-shell' 345 | else 346 | '/usr/bin/git-shell' 347 | end 348 | should contain_user('puppet') 349 | .with_shell(shell) 350 | .that_requires('Package[git]') 351 | end 352 | 353 | it do 354 | should contain_vcsrepo('puppet_repo') 355 | .with_ensure('bare') 356 | .with_path("#{vardir}/puppet.git") 357 | .with_user('puppet') 358 | .that_requires("File[#{environments_dir}]") 359 | end 360 | 361 | it do 362 | should contain_file("#{vardir}/puppet.git/hooks/post-receive") 363 | .with_owner('puppet') \ 364 | .with_mode('0755') \ 365 | .that_requires('Vcsrepo[puppet_repo]') \ 366 | .with_content(/BRANCH_MAP = \{[^a-zA-Z=>]\}/) 367 | end 368 | 369 | describe 'with a puppet git branch map' do 370 | let(:params) do 371 | super().merge(server_git_branch_map: { 'a' => 'b', 'c' => 'd' }) 372 | end 373 | 374 | it 'should add the branch map to the post receive hook' do 375 | should contain_file("#{vardir}/puppet.git/hooks/post-receive") 376 | .with_content(/BRANCH_MAP = \{\n "a" => "b",\n "c" => "d",\n\}/) 377 | end 378 | end 379 | end 380 | 381 | context 'with directory environments owner' do 382 | let(:params) { super().merge(server_environments_owner: 'apache') } 383 | it { should contain_file(environments_dir).with_owner('apache') } 384 | end 385 | 386 | context 'with directory environments recursive mangement' do 387 | let(:params) { super().merge(server_environments_recurse: true) } 388 | it { should contain_file(environments_dir).with_recurse(true) } 389 | end 390 | 391 | context 'with no common modules directory' do 392 | let(:params) { super().merge(server_common_modules_path: '') } 393 | it { should_not contain_puppet__config__main('basemodulepath') } 394 | end 395 | 396 | describe 'with SSL path overrides' do 397 | let(:params) do 398 | super().merge( 399 | server_foreman_ssl_ca: '/etc/example/ca.pem', 400 | server_foreman_ssl_cert: '/etc/example/cert.pem', 401 | server_foreman_ssl_key: '/etc/example/key.pem' 402 | ) 403 | end 404 | 405 | it 'should pass SSL parameters to the ENC' do 406 | should contain_class('puppetserver_foreman') 407 | .with_ssl_ca('/etc/example/ca.pem') 408 | .with_ssl_cert('/etc/example/cert.pem') 409 | .with_ssl_key('/etc/example/key.pem') 410 | end 411 | end 412 | 413 | describe 'with additional settings' do 414 | let(:params) do 415 | super().merge(server_additional_settings: { 'stringify_facts' => true }) 416 | end 417 | 418 | it 'should configure puppet.conf' do 419 | should contain_puppet__config__server('stringify_facts').with_value(true) 420 | end 421 | end 422 | 423 | describe 'with server_parser => future' do 424 | let(:params) do 425 | super().merge(server_parser: 'future') 426 | end 427 | 428 | it { should contain_puppet__config__server('parser').with_value('future') } 429 | end 430 | 431 | describe 'with server_environment_timeout set' do 432 | let(:params) do 433 | super().merge(server_environment_timeout: '10m') 434 | end 435 | 436 | it { should contain_puppet__config__server('environment_timeout').with_value('10m') } 437 | end 438 | 439 | describe 'with no ssldir managed for server' do 440 | let(:params) do 441 | super().merge(server_ssl_dir_manage: false) 442 | end 443 | 444 | it { should_not contain_puppet__config__server('ssl_dir') } 445 | end 446 | 447 | describe 'with ssl key management disabled for server' do 448 | let(:params) do 449 | super().merge( 450 | server_certname: 'servercert', 451 | server_ssl_dir: '/etc/custom/puppetlabs/puppet/ssl', 452 | server_ssl_key_manage: false 453 | ) 454 | end 455 | 456 | it { should_not contain_file('/etc/custom/puppetlabs/puppet/ssl/private_keys/servercert.pem') } 457 | end 458 | 459 | describe 'with nondefault CA settings' do 460 | let(:params) do 461 | super().merge(server_ca: false) 462 | end 463 | 464 | it { should contain_exec('puppet_server_config-create_ssl_dir') } 465 | it { should_not contain_exec('puppet_server_config-generate_ca_cert') } 466 | end 467 | 468 | describe 'with server_ca_crl_sync => true' do 469 | let(:params) do 470 | super().merge(server_ca_crl_sync: true) 471 | end 472 | 473 | context 'with server_ca => false and running "puppet apply"' do 474 | let(:params) do 475 | super().merge( 476 | server_ca: false, 477 | server_ssl_dir: '/etc/custom/puppetlabs/puppet/ssl' 478 | ) 479 | end 480 | 481 | it 'should not sync the crl' do 482 | # https://github.com/puppetlabs/rspec-puppet/issues/37 483 | pending("rspec-puppet always sets $server_facts['servername']") 484 | should_not contain_file('/etc/custom/puppetlabs/puppet/ssl/crl.pem') 485 | end 486 | end 487 | 488 | context 'with server_ca => false: running "puppet agent -t"' do 489 | let(:params) do 490 | super().merge( 491 | server_ca: false, 492 | server_ssl_dir: '/etc/custom/puppetlabs/puppet/ssl' 493 | ) 494 | end 495 | 496 | let(:facts) do 497 | facts.merge(servername: 'myserver') 498 | end 499 | 500 | before :context do 501 | @cacrl = Tempfile.new('cacrl') 502 | File.open(@cacrl, 'w') { |f| f.write 'This is my CRL File' } 503 | Puppet.settings[:cacrl] = @cacrl.path 504 | end 505 | 506 | it 'should sync the crl from the ca' do 507 | should contain_file('/etc/custom/puppetlabs/puppet/ssl/crl.pem') 508 | .with_content('This is my CRL File') 509 | end 510 | end 511 | 512 | context 'with server_ca => true: running "puppet agent -t"' do 513 | let(:params) do 514 | super().merge( 515 | server_ca: true, 516 | server_ssl_dir: '/etc/custom/puppetlabs/puppet/ssl' 517 | ) 518 | end 519 | 520 | let(:facts) do 521 | facts.merge(servername: 'myserver') 522 | end 523 | 524 | it 'should not sync the crl' do 525 | should_not contain_file('/etc/custom/puppetlabs/puppet/ssl/crl.pem') 526 | end 527 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(%r{path:\s*"/puppet-ca/v1/certificate_renewal"}) } 528 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(%r{path:\s*"/puppet-ca/v1/certificate_status"}) } 529 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(%r{path:\s*"/puppet-ca/v1/certificate_statuses"}) } 530 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(%r{path:\s*"/puppet-ca/v1/sign"}) } 531 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(%r{path:\s*"/puppet-ca/v1/sign/all"}) } 532 | end 533 | end 534 | 535 | describe 'allow crl checking' do 536 | context 'as ca' do 537 | let(:params) do 538 | super().merge(server_ca: true) 539 | end 540 | 541 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(%r{ssl-crl-path: #{cadir}/ca_crl\.pem}) } 542 | end 543 | 544 | context 'as non-ca' do 545 | let(:params) do 546 | super().merge(server_ca: false) 547 | end 548 | 549 | it { should contain_file("#{conf_d_dir}/webserver.conf").without_content(%r{ssl-crl-path: #{ssldir}/crl\.pem}) } 550 | 551 | context 'server_crl_enable' do 552 | let(:params) do 553 | super().merge(server_crl_enable: true) 554 | end 555 | 556 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(%r{ssl-crl-path: #{ssldir}/crl\.pem}) } 557 | end 558 | end 559 | end 560 | 561 | describe 'with ssl_protocols overwritten' do 562 | let(:params) do 563 | super().merge(server_ssl_protocols: ['TLSv1.1', 'TLSv1.2']) 564 | end 565 | 566 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(/ssl-protocols: \[\n( +)TLSv1.1,\n( +)TLSv1.2,\n( +)\]/) } 567 | end 568 | 569 | describe 'with ssl_protocols overwritten' do 570 | let(:params) do 571 | super().merge(server_cipher_suites: %w[TLS_RSA_WITH_AES_256_CBC_SHA256 TLS_RSA_WITH_AES_256_CBC_SHA]) 572 | end 573 | 574 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(/cipher-suites: \[\n( +)TLS_RSA_WITH_AES_256_CBC_SHA256,\n( +)TLS_RSA_WITH_AES_256_CBC_SHA,\n( +)\]/) } 575 | end 576 | 577 | describe 'with ssl_chain_filepath overwritten' do 578 | let(:params) do 579 | super().merge(server_ssl_chain_filepath: '/etc/example/certchain.pem') 580 | end 581 | 582 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(%r{ssl-cert-chain: /etc/example/certchain.pem}) } 583 | end 584 | 585 | describe 'with server_custom_trusted_oid_mapping overwritten' do 586 | let(:params) do 587 | super().merge(server_custom_trusted_oid_mapping: { 588 | '1.3.6.1.4.1.34380.1.2.1.1' => { 589 | shortname: 'myshortname', 590 | longname: 'My Long Name' 591 | }, 592 | '1.3.6.1.4.1.34380.1.2.1.2' => { 593 | shortname: 'myothershortname' 594 | } 595 | }) 596 | end 597 | 598 | it 'should have a configured custom_trusted_oid_mapping.yaml' do 599 | verify_exact_contents(catalogue, "#{confdir}/custom_trusted_oid_mapping.yaml", [ 600 | '---', 601 | 'oid_mapping:', 602 | ' 1.3.6.1.4.1.34380.1.2.1.1:', 603 | ' shortname: myshortname', 604 | ' longname: My Long Name', 605 | ' 1.3.6.1.4.1.34380.1.2.1.2:', 606 | ' shortname: myothershortname' 607 | ]) 608 | end 609 | end 610 | 611 | describe 'with server_certname parameter' do 612 | let(:params) do 613 | super().merge( 614 | server_certname: 'puppetserver43.example.com', 615 | server_ssl_dir: '/etc/custom/puppet/ssl' 616 | ) 617 | end 618 | 619 | it 'should put the correct ssl key path in webserver.conf' do 620 | should contain_file("#{conf_d_dir}/webserver.conf") 621 | .with_content(%r{ssl-key: /etc/custom/puppet/ssl/private_keys/puppetserver43\.example\.com\.pem}) 622 | end 623 | 624 | it 'should put the correct ssl cert path in webserver.conf' do 625 | should contain_file("#{conf_d_dir}/webserver.conf") 626 | .with_content(%r{ssl-cert: /etc/custom/puppet/ssl/certs/puppetserver43\.example\.com\.pem}) 627 | end 628 | end 629 | 630 | describe 'with server_http parameter set to true for the puppet class' do 631 | let(:params) do 632 | super().merge(server_http: true) 633 | end 634 | 635 | it { should contain_file("#{conf_d_dir}/webserver.conf").with_content(/ host:\s0\.0\.0\.0/).with_content(/ port:\s8139/) } 636 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(/allow-header-cert-info: true/) } 637 | end 638 | 639 | describe 'with server_allow_header_cert_info => true' do 640 | let(:params) do 641 | super().merge(server_allow_header_cert_info: true) 642 | end 643 | 644 | it { should contain_file("#{conf_d_dir}/auth.conf").with_content(/allow-header-cert-info: true/) } 645 | end 646 | 647 | describe 'server_trusted_external_command' do 648 | context 'with default parameters' do 649 | it { should_not contain_puppet__config__server('trusted_external_command') } 650 | end 651 | 652 | describe 'when server_trusted_external_command => /usr/local/sbin/trusted_external_command' do 653 | let(:params) do 654 | super().merge(server_trusted_external_command: '/usr/local/sbin/trusted_external_command' ) 655 | end 656 | 657 | it { should contain_puppet__config__server('trusted_external_command').with_value('/usr/local/sbin/trusted_external_command') } 658 | end 659 | end 660 | 661 | describe 'with multiple environment paths' do 662 | let(:params) do 663 | super().merge( 664 | server_envs_dir: ['/etc/puppetlabs/code/environments/', '/etc/puppetlabs/code/unmanaged-environments/'], 665 | server_git_repo_path: '/test/puppet', 666 | server_post_hook_name: 'post-receive', 667 | server_git_repo: true, 668 | ) 669 | end 670 | 671 | it { should contain_puppet__config__main('environmentpath').with_value('/etc/puppetlabs/code/environments/:/etc/puppetlabs/code/unmanaged-environments/') } 672 | it { should contain_file('/etc/puppetlabs/code/environments/') } 673 | it { should contain_file('/etc/puppetlabs/code/unmanaged-environments/') } 674 | it { should contain_vcsrepo('puppet_repo').that_requires('File[/etc/puppetlabs/code/environments/]') } 675 | it { should contain_file('/test/puppet/hooks/post-receive').with_content(/ENVIRONMENT_BASEDIR\s=\s"\/etc\/puppetlabs\/code\/environments\/"/) } 676 | end 677 | end 678 | end 679 | end 680 | --------------------------------------------------------------------------------