Menerapkan pengujian pada kode Puppet secara menyeluruh: unit testing dengan rspec-puppet dan fixtures dari puppetlabs_spec_helper, analisis statis dengan puppet-lint, validasi sintaks dengan puppet validate, hingga acceptance testing dengan Beaker di dalam pipeline CI.

Di episode 17 kalian sudah mengeksplorasi PuppetDB Query Language (PQL) dan exported resources, yang memungkinkan sebuah node membagikan resource kepada node lain secara otomatis. Kode Puppet yang dibangun sepanjang seri ini — module, class, profile, hiera data, hingga exported resources — membuat infrastruktur kalian semakin kompleks dan semakin luas pengaruhnya.
Semakin besar kode, semakin besar risiko bahwa satu perubahan kecil di satu manifest akan merusak ratusan node di produksi. Di sinilah pengujian berperan. Sebuah module tanpa test ibarat jembatan yang dibangun tanpa uji beban: kelihatan kokoh, tapi bisa runtuh saat paling dibutuhkan.
Episode ini membahas cara menguji kode Puppet secara profesional. Kita akan membahas unit testing dengan rspec-puppet dan fixtures dari puppetlabs_spec_helper, analisis statis dengan puppet-lint, validasi sintaks dengan perintah puppet validate, serta acceptance testing dengan Beaker yang bisa dijalankan otomatis di CI seperti GitHub Actions dan Jenkins.
Puppet adalah bahasa deklaratif, tetapi tetap merupakan kode program yang bisa mengandung bug: salah tipe, salah path, salah nama variabel, atau konflik antar resource. Masalahnya, bug pada kode infrastruktur tidak muncul sebagai pesan error di layar developer, melainkan sebagai gangguan layanan di lingkungan produksi.
Ada empat lapisan pengujian yang umum diterapkan pada kode Puppet:
| Lapisan | Alat | Pertanyaan yang Dijawab |
|---|---|---|
| Analisis statis | puppet-lint | Apakah gaya penulisan sudah mengikuti standar? |
| Validasi sintaks | puppet validate | Apakah kode bisa di-parse tanpa error? |
| Unit testing | rspec-puppet | Apakah class menghasilkan resource yang benar? |
| Acceptance testing | Beaker | Apakah kode benar-benar berfungsi di sistem nyata? |
Keempat lapisan tersebut saling melengkapi. Analisis statis dan validasi sintaks hanya butuh detik untuk dijalankan, sehingga cocok sebagai umpan balik cepat di setiap commit. Unit testing memberikan keyakinan terhadap logika class. Acceptance testing — meskipun paling lambat — memberikan bukti paling kuat bahwa kode berfungsi di dunia nyata.
Mulai dari yang paling cepat. puppet-lint memeriksa apakah kode kalian mengikuti Puppet Language Style Guide — misalnya ukuran indentasi, posisi tanda koma, atau penamaan file yang benar. Alat ini bisa dipasang sebagai gem Ruby:
gem install puppet-lint
puppet-lint manifests/ modules/Output yang bersih menandakan kode sudah sesuai standar. Untuk membungkam aturan yang tidak relevan bagi proyek kalian, buat file .puppet-lint.rc di root module:
--no-autoloader_layout-check
--fail-on-warningsBendera --fail-on-warnings membuat lint gagal jika ada peringatan apa pun — pas untuk dipasang di CI. Ingat, puppet-lint hanya menilai gaya, bukan kebenaran logika. Kebenaran logika menjadi urusan lapisan berikutnya.
puppet validate mem-parsing seluruh manifest tanpa mengeksekusinya. Ini adalah pengaman pertama untuk menangkap kesalahan ketik, kurung kurawal yang tidak seimbang, atau keyword yang salah:
puppet validate manifests/
puppet validate site.ppTip
Jalankan puppet validate bersamaan dengan puppet-lint sebelum setiap push. Keduanya berjalan dalam hitungan detik dan bisa menangkap sebagian besar kesalahan tingkat menulis, jauh sebelum mencapai tahap pengujian yang lebih berat.
Perintah ini juga bisa diaplikasikan pada satu file tertentu, misalnya puppet validate manifests/webserver.pp. Jika sintaks salah, Puppet akan mencetak pesan error beserta nomor baris yang bermasalah.
Unit testing adalah jantung pengujian kode Puppet. Framework yang paling umum dipakai adalah rspec-puppet, yang mengeksekusi class atau defined type di dalam lingkungan yang mensimulasikan compiler Puppet, lalu memeriksa resource apa saja yang dihasilkan.
Agar spec bisa berjalan, kalian perlu puppetlabs_spec_helper, sebuah helper yang menstandarkan struktur project testing Puppet. Helper ini otomatis mengatur path module, memuat Facter, dan mengelola fixtures. Struktur yang dihasilkan oleh PDK sudah siap pakai:
pdk new module webserver
cd webserver
pdk new class webserverPDK menghasilkan struktur dengan direktori spec/ lengkap dengan spec_helper.rb. Dependensi module yang dibutuhkan saat testing — misalnya stdlib atau concat — dideklarasikan di .fixtures.yml:
fixtures:
forge_modules:
stdlib: puppetlabs/stdlib
concat: puppetlabs/concat
symlinks:
webserver: "#{source_dir}"Note
#{source_dir} adalah variabel bawaan puppetlabs_spec_helper yang menunjuk ke direktori module. Dengan fixtures, spec hanya memuat dependensi yang benar-benar dibutuhkan, tanpa mengandalkan module yang kebetulan terpasang di mesin lokal.
Berikut contoh spec sederhana untuk class webserver yang mengelola nginx:
require 'spec_helper'
describe 'webserver' do
on_supported_os.each do |os, os_facts|
context "pada sistem operasi #{os}" do
let(:facts) { os_facts }
it { is_expected.to compile }
it { is_expected.to contain_package('nginx').with_ensure('installed') }
it { is_expected.to contain_file('/etc/nginx/conf.d/app.conf')
.with_owner('root').that_requires('Package[nginx]') }
it { is_expected.to contain_service('nginx')
.with_ensure('running').that_subscribes_to('File[/etc/nginx/conf.d/app.conf]') }
end
end
endPola it { is_expected.to ... } memeriksa matcher. Matcher compile memastikan catalog bisa dikompilasi tanpa error, sedangkan contain_package, contain_file, dan contain_service memastikan resource yang diharapkan benar-benar muncul dengan parameter yang tepat. Perhatikan penggunaan that_requires dan that_subscribes_to untuk memverifikasi relasi antar resource — persis materi episode 8.
Unit test dijalankan dengan Rake melalui puppetlabs_spec_helper:
bundle exec rake specImportant
Unit test yang baik harus mengetes behavior, bukan sekadar menyatakan tidak ada error. Biasakan menulis assertion terhadap resource yang dihasilkan — is_expected.to contain_package('nginx') jauh lebih berguna daripada hanya is_expected.to compile.
Jika assertion gagal, rspec-puppet menampilkan catalog hasil kompilasi secara lengkap, sehingga kalian bisa melihat resource yang seharusnya ada tapi hilang — atau resource yang muncul dengan parameter yang salah.
Unit testing membuktikan bahwa catalog yang dihasilkan benar, tetapi belum membuktikan bahwa hasil akhir di mesin nyata benar. Untuk itu ada Beaker: framework acceptance testing milik Puppet yang meluncurkan mesin virtual, kontainer, atau node cloud, lalu menjalankan module di sana secara nyata.
Nodeset mendeskripsikan host yang akan diuji. Berikut contoh nodeset Docker:
HOSTS:
ubuntu-agent:
platform: ubuntu-24.04-x86_64
hypervisor: docker
image: puppet/puppet-agent:8.8.1
roles:
- agent
CONFIG:
log_level: verbose
type: fossSpec acceptance ditulis sebagai RSpec biasa, contohnya:
require 'spec_helper_acceptance'
describe 'class webserver' do
let(:pp) { 'include webserver' }
it 'berfungsi tanpa error' do
apply_manifest(pp, catch_failures: true)
end
it 'bersifat idempotent' do
apply_manifest(pp, catch_changes: true)
end
endPerhatikan assertion kedua: catch_changes: true memastikan bahwa menjalankan manifest dua kali tidak menghasilkan perubahan apa pun — inilah esensi idempotency yang menjadi janji utama Puppet.
Pengujian baru berguna jika dijalankan terus-menerus. Berikut contoh workflow GitHub Actions yang menjalankan lint dan unit test pada setiap pull request:
name: Puppet CI
on:
pull_request:
push:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- run: gem install puppet puppet-lint
- run: puppet validate manifests/
- run: puppet-lint --fail-on-warnings manifests/
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
- run: bundle install
- run: bundle exec rake specUntuk Jenkins, alurnya serupa: tahapan validate dan lint berjalan cepat di setiap commit, sedangkan unit test berjalan di stage terpisah. Acceptance test dengan Beaker biasanya dijadwalkan pada pull request yang akan di-merge atau sebelum rilis, karena membutuhkan resource lebih besar dan waktu lebih lama.
bundle exec rake beakerTip
Terapkan prinsip fail fast: mulai dari puppet-lint dan puppet validate yang berjalan detik, naik ke unit test yang berjalan menit, dan akhiri dengan Beaker yang berjalan puluhan menit. Semakin mahal sebuah test, semakin jarang ia harus dijalankan — dan semakin cepat test dijalankan, semakin cepat pula developer mendapatkan umpan balik.
Pada episode ini kita telah membangun piramida pengujian untuk kode Puppet: puppet-lint untuk analisis statis, puppet validate untuk validasi sintaks, rspec-puppet dengan fixtures puppetlabs_spec_helper untuk unit testing, dan Beaker untuk acceptance testing yang membuktikan idempotency di mesin nyata. Kita juga mengintegrasikan semuanya ke GitHub Actions dan Jenkins sehingga pengujian berjalan otomatis di setiap perubahan kode.
Inti yang harus dibawa pulang:
catch_changes: true.Di episode 19 selanjutnya, kita akan membahas Performance and Troubleshooting — cara menganalisis catalog compilation dan report lag, tuning Puppet Server dari sisi JVM, serta mengatasi masalah umum seperti certificate mismatch, catalog compilation error, resource conflict, dan facts yang salah. Sampai jumpa di episode berikutnya!