diff --git a/.fixtures.yml b/.fixtures.yml new file mode 100644 index 0000000..2cf24e8 --- /dev/null +++ b/.fixtures.yml @@ -0,0 +1,11 @@ +--- +fixtures: + repositories: + stdlib: + repo: https://github.com/puppetlabs/puppetlabs-stdlib.git + ref: v9.7.0 + concat: + repo: https://github.com/puppetlabs/puppetlabs-concat.git + ref: v9.0.2 + symlinks: + sysfs: "#{source_dir}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d591d35 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI + +on: + pull_request: + branches: [master, main] + push: + branches: [master, main] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: ["3.1", "3.2", "3.3"] + puppet: ["~> 7.0", "~> 8.0"] + exclude: + - ruby: "3.3" + puppet: "~> 7.0" + + env: + PUPPET_VERSION: ${{ matrix.puppet }} + + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby ${{ matrix.ruby }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Run tests + run: bundle exec rake spec + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + bundler-cache: true + + - name: Run puppet-lint + run: bundle exec rake lint + + - name: Validate metadata.json + run: bundle exec rake metadata_lint + + - name: Validate manifests + run: bundle exec rake validate diff --git a/.gitignore b/.gitignore index 5fff1d9..9851e1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,11 @@ -pkg +pkg/ +vendor/ +.bundle/ +Gemfile.lock +spec/fixtures/ +.vagrant/ +*.swp +*.swo +.idea/ +.vscode/ +coverage/ diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..16f9cdb --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--format documentation diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..89027d2 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,24 @@ +--- +AllCops: + TargetRubyVersion: 2.6 + NewCops: enable + Exclude: + - spec/fixtures/**/* + - pkg/**/* + - vendor/**/* + +Style/Documentation: + Enabled: false + +Metrics/BlockLength: + Exclude: + - spec/**/* + +Layout/LineLength: + Max: 140 + +Style/HashSyntax: + EnforcedStyle: ruby19 + +Style/StringLiterals: + EnforcedStyle: single_quotes diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..15a2799 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.3.0 diff --git a/Gemfile b/Gemfile index 7bd34cd..f8216cc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,24 @@ source 'https://rubygems.org' -puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 3.3'] -gem 'puppet', puppetversion -gem 'puppetlabs_spec_helper', '>= 0.1.0' -gem 'puppet-lint', '>= 0.3.2' -gem 'facter', '>= 1.7.0' +puppet_version = ENV['PUPPET_VERSION'] +if puppet_version + gem 'puppet', puppet_version +else + gem 'puppet', '>= 7.0' +end +gem 'facter', '>= 2.5' + +# Required for newer Ruby versions (removed from stdlib) +gem 'logger' +gem 'ostruct' +gem 'bigdecimal' +gem 'racc' +gem 'base64' + +group :development, :test do + gem 'rake' + gem 'rspec-puppet', '~> 2.12' + gem 'puppetlabs_spec_helper', '~> 5.0' + gem 'puppet-lint', '~> 3.0' + gem 'metadata-json-lint' +end diff --git a/Rakefile b/Rakefile index d1e11f7..8d637ab 100644 --- a/Rakefile +++ b/Rakefile @@ -1,18 +1,32 @@ require 'rubygems' require 'puppetlabs_spec_helper/rake_tasks' require 'puppet-lint/tasks/puppet-lint' +require 'metadata-json-lint/rake_task' + +# Puppet-lint configuration PuppetLint.configuration.send('disable_80chars') -PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] +PuppetLint.configuration.send('disable_140chars') +PuppetLint.configuration.ignore_paths = ['spec/**/*.pp', 'pkg/**/*.pp', 'vendor/**/*.pp'] +PuppetLint.configuration.relative = true -desc "Validate manifests, templates, and ruby files" +desc 'Validate manifests, templates, and ruby files' task :validate do Dir['manifests/**/*.pp'].each do |manifest| sh "puppet parser validate --noop #{manifest}" end - Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file| - sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/ + Dir['spec/**/*.rb', 'lib/**/*.rb'].each do |ruby_file| + sh "ruby -c #{ruby_file}" unless ruby_file =~ %r{spec/fixtures} end Dir['templates/**/*.erb'].each do |template| sh "erb -P -x -T '-' #{template} | ruby -c" end end + +desc 'Run all linting tasks' +task lint_all: [:lint, :metadata_lint] + +desc 'Run all tests' +task test: [:validate, :lint_all, :spec] + +# Default task +task default: :test diff --git a/metadata.json b/metadata.json index 684c097..640d151 100644 --- a/metadata.json +++ b/metadata.json @@ -3,13 +3,50 @@ "version": "0.2.0", "author": "danfoster", "summary": "A module for managing sysfs settings", - "license": "Apache 2.0", + "license": "Apache-2.0", "source": "https://github.com/danfoster/puppet-sysfs", "project_page": "https://github.com/danfoster/puppet-sysfs", "issues_url": "https://github.com/danfoster/puppet-sysfs/issues", "dependencies": [ - {"name":"puppetlabs-stdlib","version_requirement":">= 1.0.0"}, - {"name":"puppetlabs-concat"} + { + "name": "puppetlabs-stdlib", + "version_requirement": ">= 4.0.0 < 10.0.0" + }, + { + "name": "puppetlabs-concat", + "version_requirement": ">= 4.0.0 < 10.0.0" + } + ], + "operatingsystem_support": [ + { + "operatingsystem": "RedHat", + "operatingsystemrelease": ["7", "8", "9"] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": ["7", "8"] + }, + { + "operatingsystem": "AlmaLinux", + "operatingsystemrelease": ["8", "9"] + }, + { + "operatingsystem": "Rocky", + "operatingsystemrelease": ["8", "9"] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": ["10", "11", "12"] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": ["20.04", "22.04", "24.04"] + } + ], + "requirements": [ + { + "name": "puppet", + "version_requirement": ">= 7.0.0 < 9.0.0" + } ] } - diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb deleted file mode 100644 index bc28aa0..0000000 --- a/spec/classes/init_spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'spec_helper' -describe 'sysfs' do - - context 'with defaults for all parameters' do - it { should contain_class('sysfs') } - end -end diff --git a/spec/classes/sysfs_spec.rb b/spec/classes/sysfs_spec.rb new file mode 100644 index 0000000..ff98d4d --- /dev/null +++ b/spec/classes/sysfs_spec.rb @@ -0,0 +1,184 @@ +require 'spec_helper' + +describe 'sysfs' do + context 'with default parameters' do + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('sysfs') } + + # Package management + it { is_expected.to contain_package('sysfsutils').with_ensure('installed') } + + # Configuration file + it do + is_expected.to contain_concat('/etc/sysfs.conf').with( + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'force' => true, + ) + end + + it { is_expected.to contain_concat('/etc/sysfs.conf').that_requires('Package[sysfsutils]') } + + # Service management + it do + is_expected.to contain_service('sysfsutils').with( + 'ensure' => 'running', + 'enable' => true, + ) + end + + it { is_expected.to contain_service('sysfsutils').that_subscribes_to('Concat[/etc/sysfs.conf]') } + end + + context 'with settings parameter' do + let(:params) do + { + settings: { + 'class/block/sda/queue/read_ahead_kb' => { 'value' => '256' }, + 'class/block/sdb/queue/rotational' => { 'value' => '0' }, + }, + } + end + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_sysfs__setting('class/block/sda/queue/read_ahead_kb').with( + 'value' => '256', + ) + end + + it do + is_expected.to contain_sysfs__setting('class/block/sdb/queue/rotational').with( + 'value' => '0', + ) + end + end + + # RedHat 7+ specific tests + context 'on RedHat 7+' do + let(:facts) do + { + os: { + 'family' => 'RedHat', + 'name' => 'CentOS', + 'release' => { + 'full' => '7.9', + 'major' => '7', + 'minor' => '9', + }, + }, + kernel: 'Linux', + } + end + + it { is_expected.to compile.with_all_deps } + + # Custom reload script for RHEL 7+ + it do + is_expected.to contain_file('/usr/local/bin/sysfs-reload').with( + 'source' => 'puppet:///modules/sysfs/sysfs-reload', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0700', + ) + end + + it { is_expected.to contain_file('/usr/local/bin/sysfs-reload').that_comes_before('File[/etc/systemd/system/sysfsutils.service]') } + + # Systemd service unit + it do + is_expected.to contain_file('/etc/systemd/system/sysfsutils.service').with( + 'source' => 'puppet:///modules/sysfs/sysfsutils.service', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0700', + ) + end + + it { is_expected.to contain_file('/etc/systemd/system/sysfsutils.service').that_comes_before('Service[sysfsutils]') } + + # Exec for reloading sysfs settings + it do + is_expected.to contain_exec('sysfsutils_reload_rhel').with( + 'refreshonly' => true, + ) + end + + it { is_expected.to contain_exec('sysfsutils_reload_rhel').that_subscribes_to('Concat[/etc/sysfs.conf]') } + end + + context 'on RedHat 8+' do + let(:facts) do + { + os: { + 'family' => 'RedHat', + 'name' => 'AlmaLinux', + 'release' => { + 'full' => '8.5', + 'major' => '8', + 'minor' => '5', + }, + }, + kernel: 'Linux', + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_file('/usr/local/bin/sysfs-reload') } + it { is_expected.to contain_file('/etc/systemd/system/sysfsutils.service') } + it { is_expected.to contain_exec('sysfsutils_reload_rhel') } + end + + context 'on RedHat 6 (pre-systemd)' do + let(:facts) do + { + os: { + 'family' => 'RedHat', + 'name' => 'CentOS', + 'release' => { + 'full' => '6.10', + 'major' => '6', + 'minor' => '10', + }, + }, + kernel: 'Linux', + } + end + + it { is_expected.to compile.with_all_deps } + it { is_expected.not_to contain_file('/usr/local/bin/sysfs-reload') } + it { is_expected.not_to contain_file('/etc/systemd/system/sysfsutils.service') } + it { is_expected.not_to contain_exec('sysfsutils_reload_rhel') } + end + + context 'on Debian' do + let(:facts) do + { + os: { + 'family' => 'Debian', + 'name' => 'Ubuntu', + 'release' => { + 'full' => '22.04', + 'major' => '22', + 'minor' => '04', + }, + }, + kernel: 'Linux', + } + end + + it { is_expected.to compile.with_all_deps } + + # Should NOT have RHEL-specific resources + it { is_expected.not_to contain_file('/usr/local/bin/sysfs-reload') } + it { is_expected.not_to contain_file('/etc/systemd/system/sysfsutils.service') } + it { is_expected.not_to contain_exec('sysfsutils_reload_rhel') } + + # Should still have common resources + it { is_expected.to contain_package('sysfsutils') } + it { is_expected.to contain_service('sysfsutils') } + it { is_expected.to contain_concat('/etc/sysfs.conf') } + end +end diff --git a/spec/defines/setting_spec.rb b/spec/defines/setting_spec.rb new file mode 100644 index 0000000..a56c6f2 --- /dev/null +++ b/spec/defines/setting_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +describe 'sysfs::setting' do + let(:pre_condition) { 'include sysfs' } + + let(:facts) do + { + os: { + 'family' => 'RedHat', + 'name' => 'CentOS', + 'release' => { + 'full' => '8.0', + 'major' => '8', + 'minor' => '0', + }, + }, + kernel: 'Linux', + } + end + + context 'with a simple setting' do + let(:title) { 'class/block/sda/queue/read_ahead_kb' } + let(:params) { { value: '256' } } + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('class/block/sda/queue/read_ahead_kb').with( + 'target' => '/etc/sysfs.conf', + 'content' => "class/block/sda/queue/read_ahead_kb=256\n", + ) + end + end + + context 'with rotational setting' do + let(:title) { 'class/block/sdb/queue/rotational' } + let(:params) { { value: '0' } } + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('class/block/sdb/queue/rotational').with( + 'target' => '/etc/sysfs.conf', + 'content' => "class/block/sdb/queue/rotational=0\n", + ) + end + end + + context 'with scheduler setting' do + let(:title) { 'block/sda/queue/scheduler' } + let(:params) { { value: 'deadline' } } + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('block/sda/queue/scheduler').with( + 'target' => '/etc/sysfs.conf', + 'content' => "block/sda/queue/scheduler=deadline\n", + ) + end + end + + context 'with numeric value' do + let(:title) { 'kernel/mm/transparent_hugepage/enabled' } + let(:params) { { value: 'never' } } + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('kernel/mm/transparent_hugepage/enabled').with( + 'target' => '/etc/sysfs.conf', + 'content' => "kernel/mm/transparent_hugepage/enabled=never\n", + ) + end + end + + context 'with integer value' do + let(:title) { 'class/net/eth0/queues/rx-0/rps_cpus' } + let(:params) { { value: 15 } } + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('class/net/eth0/queues/rx-0/rps_cpus').with( + 'target' => '/etc/sysfs.conf', + 'content' => "class/net/eth0/queues/rx-0/rps_cpus=15\n", + ) + end + end + + context 'on Debian' do + let(:facts) do + { + os: { + 'family' => 'Debian', + 'name' => 'Ubuntu', + 'release' => { + 'full' => '22.04', + 'major' => '22', + 'minor' => '04', + }, + }, + kernel: 'Linux', + } + end + + let(:title) { 'class/block/sda/queue/nr_requests' } + let(:params) { { value: '128' } } + + it { is_expected.to compile.with_all_deps } + + it do + is_expected.to contain_concat__fragment('class/block/sda/queue/nr_requests').with( + 'target' => '/etc/sysfs.conf', + 'content' => "class/block/sda/queue/nr_requests=128\n", + ) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2c6f566..297a152 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1 +1,24 @@ require 'puppetlabs_spec_helper/module_spec_helper' + +RSpec.configure do |c| + c.default_facts = { + os: { + 'family' => 'RedHat', + 'name' => 'CentOS', + 'release' => { + 'full' => '8.0', + 'major' => '8', + 'minor' => '0', + }, + }, + osfamily: 'RedHat', + operatingsystem: 'CentOS', + operatingsystemrelease: '8.0', + kernel: 'Linux', + } + + c.before(:each) do + Puppet::Util::Log.level = :warning + Puppet::Util::Log.newdestination(:console) + end +end