Belajar Puppet - Testing
Episode 18 of 23

Belajar Puppet - Testing

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.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Pendahuluan

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.

Mengapa Testing Penting untuk Kode Infrastruktur

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:

LapisanAlatPertanyaan yang Dijawab
Analisis statispuppet-lintApakah gaya penulisan sudah mengikuti standar?
Validasi sintakspuppet validateApakah kode bisa di-parse tanpa error?
Unit testingrspec-puppetApakah class menghasilkan resource yang benar?
Acceptance testingBeakerApakah 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.

Analisis Statis dengan puppet-lint

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:

Install dan jalankan puppet-lint
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:

Contoh .puppet-lint.rc
--no-autoloader_layout-check
--fail-on-warnings

Bendera --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.

Validasi Sintaks dengan puppet validate

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:

Validasi seluruh manifest
puppet validate manifests/
puppet validate site.pp

Tip

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 dengan rspec-puppet

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.

Menyiapkan Fixtures dengan puppetlabs_spec_helper

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:

Membuat project module dengan PDK
pdk new module webserver
cd webserver
pdk new class webserver

PDK 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.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:

spec/classes/webserver_spec.rb
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
end

Pola 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.

Menjalankan Unit Test

Unit test dijalankan dengan Rake melalui puppetlabs_spec_helper:

bundle exec rake spec

Important

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.

Acceptance Testing dengan Beaker

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:

spec/acceptance/nodesets/default.yml
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: foss

Spec acceptance ditulis sebagai RSpec biasa, contohnya:

spec/acceptance/webserver_spec.rb
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
end

Perhatikan assertion kedua: catch_changes: true memastikan bahwa menjalankan manifest dua kali tidak menghasilkan perubahan apa pun — inilah esensi idempotency yang menjadi janji utama Puppet.

Integrasi di CI/CD

Pengujian baru berguna jika dijalankan terus-menerus. Berikut contoh workflow GitHub Actions yang menjalankan lint dan unit test pada setiap pull request:

.github/workflows/ci.yml
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 spec

Untuk 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.

Menjalankan acceptance test
bundle exec rake beaker

Tip

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.

Penutup

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:

  • Buat test menjadi bagian dari alur kerja — bukan kegiatan terpisah; jalankan di setiap commit lewat CI.
  • Gunakan PDK sebagai generator — struktur spec, fixtures, dan Rakefile yang benar sudah dibuatkan otomatis.
  • Uji behavior, bukan sekadar tidak error — assertion terhadap resource dan parameter jauh lebih bernilai.
  • Idempotency adalah janji Puppet — verifikasi dengan Beaker lewat catch_changes: true.
  • Gradasi biaya — cepat dan murah dulu untuk lint dan validate, mahal belakangan untuk unit dan acceptance.

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!

Belajar Puppet - Testing | Belajar Puppet