<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: betadots</title>
    <description>The latest articles on DEV Community by betadots (betadots).</description>
    <link>https://dev.to/betadots</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4564%2F6344c1dc-d9d3-4988-baf0-72cc46b83aa7.jpg</url>
      <title>DEV Community: betadots</title>
      <link>https://dev.to/betadots</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/betadots"/>
    <language>en</language>
    <item>
      <title># OpenVox 9 ist da!</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:48:53 +0000</pubDate>
      <link>https://dev.to/betadots/-openvox-9-ist-da-354</link>
      <guid>https://dev.to/betadots/-openvox-9-ist-da-354</guid>
      <description>&lt;p&gt;Am 2. Oktober 2026 hat die OpenVox-Project-Community das Release 9.0 von OpenVox veröffentlicht.&lt;br&gt;
Damit basiert die Community-Version auf demselben Unterbau wie &lt;a href="https://www.puppet.com/products/puppet-core" rel="noopener noreferrer"&gt;Puppet Core 9&lt;/a&gt;, das im August 2026 erschienen ist.&lt;/p&gt;

&lt;p&gt;Der folgende Text ist eine Übersetzung mit kleineren Anpassungen vom &lt;a href="https://voxpupuli.org/blog/2026/10/02/openvox-9-release/" rel="noopener noreferrer"&gt;Release Blog Posting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wir haben soeben OpenVox 9.0.0 veröffentlicht!&lt;/strong&gt; Hierbei handelt es sich um eine Aktualisierung der Plattformbasis; das bedeutet, dass alle Kernabhängigkeiten auf aktiv gepflegte Versionen umgestellt wurden. Wer die jüngste Flut an CVE-Meldungen mit Sorge verfolgt hat, für den ist dieses Release genau das Richtige.&lt;/p&gt;

&lt;p&gt;OpenVox 9 umfasst &lt;code&gt;openvox-agent&lt;/code&gt; und &lt;code&gt;openvoxdb&lt;/code&gt; in Version 9.0.0 sowie &lt;code&gt;openvox-server&lt;/code&gt; in Version 9.0.1, ergänzt durch &lt;code&gt;openfact&lt;/code&gt; 6.2.1.&lt;br&gt;
(Das erste Release von &lt;code&gt;openvox-server&lt;/code&gt; trägt die Nummer 9.0.1, da die Version 9.0.0 bereits durch ein Artefakt belegt war, das während der Beta-Phase versehentlich auf Clojars veröffentlicht worden war.)&lt;br&gt;
Zudem wurde eine Reihe &lt;a href="https://voxpupuli.org/blog/2026/04/14/openvox-9-request-for-comments/" rel="noopener noreferrer"&gt;längst veralteter Code-Bestände entfernt&lt;/a&gt;.&lt;br&gt;
Neue Funktionen sind für die Version 10.x geplant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release-Hinweise und Dokumentation
&lt;/h2&gt;

&lt;p&gt;Die vollständigen Release-Hinweise, einschließlich Fehlerbehebungen und der Änderungen in den jeweiligen Vorabversionen, findet man hier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;openvox-agent&lt;/code&gt;: &lt;a href="https://github.com/OpenVoxProject/openvox/releases/tag/9.0.0" rel="noopener noreferrer"&gt;https://github.com/OpenVoxProject/openvox/releases/tag/9.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openvox-server&lt;/code&gt;: &lt;a href="https://github.com/OpenVoxProject/openvox-server/releases/tag/9.0.1" rel="noopener noreferrer"&gt;https://github.com/OpenVoxProject/openvox-server/releases/tag/9.0.1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openvoxdb&lt;/code&gt;: &lt;a href="https://github.com/OpenVoxProject/openvoxdb/releases/tag/9.0.0" rel="noopener noreferrer"&gt;https://github.com/OpenVoxProject/openvoxdb/releases/tag/9.0.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openfact&lt;/code&gt;: &lt;a href="https://github.com/OpenVoxProject/openfact/releases/tag/6.0.0" rel="noopener noreferrer"&gt;https://github.com/OpenVoxProject/openfact/releases/tag/6.0.0&lt;/a&gt; (OpenVox 9.0.0 enthält Version 6.2.1, die lediglich Fehlerbehebungen ergänzt)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Die Dokumentation zu OpenVox 9 findet man unter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenVox: &lt;a href="https://docs.openvoxproject.org/openvox/9.x/" rel="noopener noreferrer"&gt;https://docs.openvoxproject.org/openvox/9.x/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenVox Server: &lt;a href="https://docs.openvoxproject.org/openvox-server/9.x/" rel="noopener noreferrer"&gt;https://docs.openvoxproject.org/openvox-server/9.x/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenVoxDB: &lt;a href="https://docs.openvoxproject.org/openvoxdb/9.x/" rel="noopener noreferrer"&gt;https://docs.openvoxproject.org/openvoxdb/9.x/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OpenVox 9 beziehen
&lt;/h2&gt;

&lt;p&gt;OpenVox-9-Pakete werden unter der Bezeichnung &lt;code&gt;openvox9&lt;/code&gt; bereitgestellt.&lt;br&gt;
Um diese Repositories zu konfigurieren, muss das Paket &lt;code&gt;openvox9-release&lt;/code&gt; installiert werden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://apt.voxpupuli.org/" rel="noopener noreferrer"&gt;https://apt.voxpupuli.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yum.voxpupuli.org/" rel="noopener noreferrer"&gt;https://yum.voxpupuli.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pakete für macOS und Windows findet man unter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://downloads.voxpupuli.org/mac/openvox9/" rel="noopener noreferrer"&gt;https://downloads.voxpupuli.org/mac/openvox9/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://downloads.voxpupuli.org/windows/openvox9/" rel="noopener noreferrer"&gt;https://downloads.voxpupuli.org/windows/openvox9/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wenn Beta-Versionen oder Release-Candidates getestet wurden, gelanget man durch ein normales Paket-Upgrade zur finalen Version.&lt;br&gt;
Vorabversionen enthielten eine Tilde in der Versionsnummer (z. B. &lt;code&gt;9.0.0~rc4&lt;/code&gt;), damit sie bei der Sortierung unterhalb der finalen Version eingeordnet wurden.&lt;/p&gt;

&lt;p&gt;OpenVox 8 bleibt über die &lt;code&gt;openvox8&lt;/code&gt;-Repositories verfügbar. Es wird weiterhin für mindestens sechs Monate wichtige Fehlerbehebungen und Sicherheits-Updates erhalten.&lt;br&gt;
OpenVox-8-Agents können weiterhin mit OpenVox-9-Servern kommunizieren. Man muss nicht alles auf einmal umstellen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wichtigste Änderungen im Vergleich zu OpenVox 8
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;openvox-agent&lt;/code&gt; &lt;strong&gt;Ruby 4.0&lt;/strong&gt; (zuvor Ruby 3.2) und &lt;strong&gt;OpenSSL 3.5 LTS&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openvox-server&lt;/code&gt; verwendet &lt;strong&gt;JRuby 10.1&lt;/strong&gt;, das mit Ruby 4.0 kompatibel ist. Dies ist ein Upgrade von JRuby 9.4, das mit Ruby 3.1 kompatibel war.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openvox-server&lt;/code&gt; und &lt;code&gt;openvoxdb&lt;/code&gt; werden offiziell unter &lt;strong&gt;Java 21 und 25&lt;/strong&gt; unterstützt und getestet; die Unterstützung für Java 17 wurde eingestellt. 
Die Pakete laufen unter Java 25, sofern die Plattform dies bereitstellt, andernfalls unter Java 21. Die FIPS-Pakete laufen &lt;strong&gt;nur unter Java 21&lt;/strong&gt;, da die Bouncy-Castle-FIPS-Bibliotheken nur bis Java 21 zertifiziert sind. Der Service nutzt jetzt den Paketpfad und nicht die Alternative (&lt;code&gt;/usr/bin/java&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openvoxdb&lt;/code&gt; wird nun mit &lt;strong&gt;PostgreSQL 18&lt;/strong&gt; getestet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openvox-agent&lt;/code&gt; enthält &lt;strong&gt;openfact 6&lt;/strong&gt;; damit entfällt die Fact-Option &lt;code&gt;ldapname&lt;/code&gt;, und es werden Warnungen zu künftigen Entfernungen (geplant für OpenVox 10) ausgegeben.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vor dem Upgrade
&lt;/h2&gt;

&lt;p&gt;Dies ist ein Major-Release mit Breaking Changes (Änderungen, die bestehende Konfigurationen oder Abläufe beeinträchtigen können).&lt;br&gt;
Bitte die Release Notes vollständig durchlesen und die &lt;a href="https://github.com/OpenVoxProject/openvox/releases/tag/9.0.0" rel="noopener noreferrer"&gt;OpenVox 9.0.0 Release Notes&lt;/a&gt; beachten.&lt;/p&gt;

&lt;p&gt;Die wichtigsten Informationen sind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Der Standardwert für &lt;code&gt;server&lt;/code&gt; ist nicht mehr &lt;code&gt;puppet&lt;/code&gt;.&lt;/strong&gt;
Agents müssen &lt;code&gt;server&lt;/code&gt; oder &lt;code&gt;server_list&lt;/code&gt; konfigurieren oder SRV-Records verwenden (auch &lt;code&gt;ca_server&lt;/code&gt; und &lt;code&gt;report_server&lt;/code&gt; funktionieren für die jeweiligen Dienste). 
Ein Agent, der als Root ausgeführt wird, ohne dass eine dieser Optionen gesetzt ist, bricht mit einem Fehler ab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Die Einstellung &lt;code&gt;reports&lt;/code&gt; hat nun den Standardwert &lt;code&gt;none&lt;/code&gt;&lt;/strong&gt; anstelle von &lt;code&gt;store&lt;/code&gt;. 
Setzen Sie &lt;code&gt;reports = store&lt;/code&gt; auf Ihren Servern, wenn Sie darauf angewiesen sind, dass Berichte auf der Festplatte gespeichert werden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datenbindungen aus der Hiera-3-Ära sind entfallen.&lt;/strong&gt;
Die Einstellungen &lt;code&gt;data_binding_terminus&lt;/code&gt; und &lt;code&gt;environment_data_provider&lt;/code&gt; sowie der Hiera-3-Indirector-Terminus wurden entfernt; Hiera 5 ist der unterstützte Standard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java 21 ist nun erforderlich.&lt;/strong&gt; Wenn die Dienste unter Java 17 starten, stürzen sie frühzeitig mit einer &lt;code&gt;ClassNotFoundException&lt;/code&gt; für &lt;code&gt;java.util.SequencedCollection&lt;/code&gt; ab.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bekannte Probleme
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Agent-Daemon kann gelegentlich beim Status „applying configuration“ hängen bleiben
&lt;/h3&gt;

&lt;p&gt;Bei einer kleinen Anzahl von Nutzern wurde beobachtet, dass der &lt;code&gt;puppet agent&lt;/code&gt;-Daemon keine Statusmeldungen mehr sendet, während ein per &lt;code&gt;fork&lt;/code&gt; gestarteter Prozess mit der Bezeichnung &lt;code&gt;puppet agent: applying configuration&lt;/code&gt; aktiv bleibt und sich nicht beendet (&lt;a href="https://github.com/OpenVoxProject/openvox/issues/485" rel="noopener noreferrer"&gt;openvox#485&lt;/a&gt;).&lt;br&gt;
Dies tritt selten auf: Die bisherigen Meldungen stammen von kleinen Linux-VMs mit nur einer vCPU, bei denen das Problem alle paar Tage auftrat.&lt;/p&gt;

&lt;p&gt;Die Ursache ist eine seltene Race-Condition zwischen den Hintergrund-Threads für DNS-Auflösungen in Ruby (deren Verhalten sich in Ruby 3.3 und 3.4 geändert hat) und dem &lt;code&gt;fork&lt;/code&gt;-Aufruf, der jeden Agent-Lauf einleitet.&lt;br&gt;
Erfolgt der &lt;code&gt;fork&lt;/code&gt; im ungünstigen Moment, übernimmt der Kindprozess eine gesperrte glibc-Resolver-Instanz (Mutex), wodurch jede Namensauflösung in diesem Kindprozess blockiert wird.&lt;br&gt;
OpenVox 8 (Ruby 3.2) ist davon nicht betroffen.&lt;/p&gt;

&lt;p&gt;OpenVox 9.0.0 enthält mehrere Änderungen, die die Wahrscheinlichkeit dieses Problems stark verringern und sicherstellen, dass sich der Daemon selbstständig erholt, sollte es dennoch auftreten:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Der Agent führt im Daemon keine Namensauflösungen mehr unmittelbar vor dem &lt;code&gt;fork&lt;/code&gt;-Aufruf für einen Lauf durch (&lt;a href="https://github.com/OpenVoxProject/openvox/pull/683" rel="noopener noreferrer"&gt;openvox#683&lt;/a&gt;); damit entfällt der in den Berichten identifizierte Auslöser.&lt;/li&gt;
&lt;li&gt;Ein Lauf, der die Zeitvorgabe &lt;code&gt;runtimeout&lt;/code&gt; überschreitet, wird nun beendet (abgebrochen), sodass der Daemon mit dem nächsten Lauf fortfahren kann (&lt;a href="https://github.com/OpenVoxProject/openvox/pull/642" rel="noopener noreferrer"&gt;openvox#642&lt;/a&gt;, &lt;a href="https://github.com/OpenVoxProject/openvox/pull/643" rel="noopener noreferrer"&gt;openvox#643&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;openfact begrenzt die FQDN-Auflösung in seinen Hostname-Resolvern (&lt;a href="https://github.com/OpenVoxProject/openfact/pull/177" rel="noopener noreferrer"&gt;openfact#177&lt;/a&gt;). Falls dieses Problem weiterhin auftritt, kann das Setzen von &lt;code&gt;RUBY_TCP_NO_FAST_FALLBACK=1&lt;/code&gt; in einer systemd-Override-Konfiguration für den &lt;code&gt;puppet&lt;/code&gt;-Dienst die Häufigkeit des Auftretens verringern:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# systemctl edit puppet
&lt;/span&gt;&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;RUBY_TCP_NO_FAST_FALLBACK=1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ein Neustart des &lt;code&gt;puppet&lt;/code&gt;-Dienstes bereinigt keinen bereits hängengebliebenen Kindprozess; dieser muss mittels &lt;code&gt;SIGKILL&lt;/code&gt; beendet werden.&lt;br&gt;
Sollte das Problem bei Version 9.0.0 aufstoßen, bitte im Issue &lt;a href="https://github.com/OpenVoxProject/openvox/issues/485" rel="noopener noreferrer"&gt;openvox#485&lt;/a&gt; Informationen hinterlegen.&lt;/p&gt;

&lt;h3&gt;
  
  
  FIPS-Pakete enthalten ältere Bouncy-Castle-JAR-Dateien
&lt;/h3&gt;

&lt;p&gt;Die FIPS-Builds von &lt;code&gt;openvox-server&lt;/code&gt; und &lt;code&gt;openvoxdb&lt;/code&gt; enthalten einen älteren Satz von Bouncy-Castle-FIPS-JAR-Dateien.&lt;br&gt;
Die neueste Version der Bouncy-Castle-FIPS-1.x-Reihe weist ein kleines Problem auf, das in einer stark eingeschränkten Konfiguration (außerhalb der Standardeinstellungen) zu Schwierigkeiten führen könnte; daher führen wir kein Update auf diese Version durch.&lt;br&gt;
Stattdessen wird OpenVox 9.1 auf die Bouncy-Castle-FIPS-2.x-Reihe umsteigen, welche für FIPS unter Java 21 vollständig zertifiziert ist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vielen Dank
&lt;/h2&gt;

&lt;p&gt;OpenVox 9 würde ohne all jene nicht existieren, die Pull-Requests eingereicht, Fehler gemeldet, Vorabversionen getestet und an Diskussionen über anstehende Änderungen teilgenommen haben.&lt;br&gt;
Vielen Dank an alle!&lt;/p&gt;

&lt;p&gt;Ein besonderer Dank gilt den Personen, die in diesem Entwicklungszyklus einen Großteil der Arbeit geleistet haben, darunter unter anderem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/bastelfreak" rel="noopener noreferrer"&gt;Tim Meusel&lt;/a&gt; für seine Arbeit an nahezu allen Repositories: Aktualisierungen von Laufzeitumgebungen und Abhängigkeiten, CI, Release-PRs sowie SBOMs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Sharpie" rel="noopener noreferrer"&gt;Charlie Sharpsteen&lt;/a&gt;, für seine Arbeit an Server, Datenbank, Laufzeitumgebung und Build-Tools sowie für die umfassende Überarbeitung der Dokumentation („Great Docs Reset“).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/miharp" rel="noopener noreferrer"&gt;Michael Harp&lt;/a&gt;, für den enormen Umfang an Dokumentationsarbeit – einschließlich der Unterlagen für Version 9.x – sowie für die Ermittlung der Ursache des oben beschriebenen Agenten-Hängers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nmburgan" rel="noopener noreferrer"&gt;Nick Burgan&lt;/a&gt;, für den Ausbau der Release-Infrastruktur, Smoke-Tests sowie die Arbeit an der Paketierung für Java und FIPS.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/corporate-gadfly" rel="noopener noreferrer"&gt;Haroon Rafique&lt;/a&gt;, für die Pflege der Clojure-Komponenten von &lt;code&gt;openvox-server&lt;/code&gt; und &lt;code&gt;openvoxdb&lt;/code&gt;, FIPS-Korrekturen und mehr. Sichere Standardeinstellung für &lt;code&gt;server&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tuxmea" rel="noopener noreferrer"&gt;Martin Alfke&lt;/a&gt;, für die Aktualisierung großer Teile der Dokumentation für OpenVox und OpenFact.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rwaffen" rel="noopener noreferrer"&gt;Robert Waffen&lt;/a&gt;, für die Neugestaltung der Container-Images und die Einführung von Multi-Plattform-Builds.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/binford2k" rel="noopener noreferrer"&gt;Ben Ford&lt;/a&gt;, für die „Getting-Started“- und „Quickstart“-Anleitungen, die Dokumentations-Tools sowie die Katalogvalidierung im Agenten.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jcharaoui" rel="noopener noreferrer"&gt;Jerome Charaoui&lt;/a&gt;, für die Debian-Paketierung und Korrekturen zur Reproduzierbarkeit.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jpartlow" rel="noopener noreferrer"&gt;Josh Partlow&lt;/a&gt;, für die arm64-Unterstützung und weitere Arbeiten an den Abnahmetests.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/austb" rel="noopener noreferrer"&gt;Austin Blatt&lt;/a&gt;, für die Migration auf Jetty 12.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bootc" rel="noopener noreferrer"&gt;Chris Boot&lt;/a&gt;, für wochenlange, geduldige Debugging-Arbeit im Zusammenhang mit &lt;a href="https://github.com/OpenVoxProject/openvox/issues/485" rel="noopener noreferrer"&gt;openvox#485&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/MirandaStreeter" rel="noopener noreferrer"&gt;Miranda Streeter&lt;/a&gt;, für die Verbesserungen der Startgeschwindigkeit der Container-Images.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Bei Fragen oder Problemen mit diesen Releases bitte im &lt;code&gt;#openvox&lt;/code&gt; Channel auf Slack oder &lt;code&gt;#voxpupuli-openvox&lt;/code&gt; im IRC melden.&lt;br&gt;
Details findet man unter &lt;a href="https://voxpupuli.org/connect/" rel="noopener noreferrer"&gt;https://voxpupuli.org/connect/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>openvox</category>
      <category>puppet</category>
      <category>release</category>
    </item>
    <item>
      <title>DevConf Tag 2</title>
      <dc:creator>Marcus</dc:creator>
      <pubDate>Sat, 20 Jun 2026 07:43:19 +0000</pubDate>
      <link>https://dev.to/betadots/devconf-tag-2-250o</link>
      <guid>https://dev.to/betadots/devconf-tag-2-250o</guid>
      <description>&lt;p&gt;Es ist 9Uhr morgens und der Kaffeestand hat mich vertröstet mit den Worten "Wir sind geschlossen, Eröffnung ist 09:30Uhr"&lt;/p&gt;

&lt;p&gt;Unter dem einzigen schattenspenden Baum im Hof sammeln sich die Konferenzteilnehmer.&lt;/p&gt;

&lt;p&gt;Ich habe mir einen glibc-Workshop und einen Quadlets-Workshop vorgenommen.&lt;/p&gt;

&lt;h2&gt;
  
  
  glibc-Workshop
&lt;/h2&gt;

&lt;p&gt;Bild: A duck for a bug.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8lm4wrwrwzml0vc7cbj0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8lm4wrwrwzml0vc7cbj0.jpg" alt=" " width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aufgabe war: Ich habe hier 20 einfache Bugs in glibc heraus gesucht. Wir haben hier drei Mentoren, die an der glibc gearbeitet haben. Ich werde die Bugs jetzt an die Teilnehmer versteigern und wer als erster einen Patch einreicht, gewinnt die Ente.&lt;/p&gt;

&lt;p&gt;Der Leiter scherzte noch: Mich hat die Vorbereitung acht Stunden gekostet. Wenn auch nur einer von euch dran bleibt und weiter Patches einreicht, habe ich ein beeindruckendes Verhältnis zwischen Aufwand und Nutzen erreicht. :-)&lt;br&gt;
Leider ist glibc nicht so ruhmreich wie der Kernel und hat keine Vollzeitentwickler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Podman-Quadlets Workshop
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codeberg.org/inknos/DevConf26/src/branch/main/demo/01_wordpress/03/README.md" rel="noopener noreferrer"&gt;https://codeberg.org/inknos/DevConf26/src/branch/main/demo/01_wordpress/03/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Die Demos waren schön und zeigen, wie man von Docker-Compose auf Podman kommt oder von Quadlets auf Kubernetes oder von Kubernetes auf  Quadlets. Quadlets haben Potential, wenn sie etwas Liebe erfahren. &lt;/p&gt;

&lt;p&gt;Podman Quadlets funktionieren unter Debian nicht. Dafür habe ich mit einem der RedHat-Entwickler gemeinsam einen Bug-Report erstellt. Am Workshop teilgenommen habe ich mit einer Fedora-VM.&lt;br&gt;
Der Theorie nach bringt Podman in Form von &lt;code&gt;podman machine init&lt;/code&gt; und &lt;code&gt;podman machine start&lt;/code&gt; eine VM mit, die quadletfähig sein soll, aber die ist unter Debian ebenfalls defekt.&lt;/p&gt;

</description>
      <category>devconf</category>
      <category>conference</category>
      <category>it</category>
    </item>
    <item>
      <title>DevConf: Tag 1</title>
      <dc:creator>Marcus</dc:creator>
      <pubDate>Thu, 18 Jun 2026 07:54:44 +0000</pubDate>
      <link>https://dev.to/betadots/devconf-tag-1-2bf</link>
      <guid>https://dev.to/betadots/devconf-tag-1-2bf</guid>
      <description>&lt;p&gt;Die Genese der DevConf: RedHat hat seinen europäisches Hauptquartier in Brno/Tschechien.&lt;/p&gt;

&lt;p&gt;Möchtest du life mitschauen? &lt;a href="https://www.youtube.com/@DevConfs" rel="noopener noreferrer"&gt;https://www.youtube.com/@DevConfs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ich möchte die vergangenen Vorträge dokumentieren:&lt;/p&gt;

&lt;p&gt;09:00 Einleitung:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wenn ihr zum ersten Mal hier seid, nehmt die Karte und probiert die Räume ohne Hilfe zu finden
&lt;a href="https://www.devconf.info/cz/map/" rel="noopener noreferrer"&gt;https://www.devconf.info/cz/map/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;09:30 CVE-Vortrag. RedHat hat ein Rolling-Container-Release mit AI gebaut, sie nennen es "Hummingbird".&lt;br&gt;
"If you have to explain it [CVEs to your manager], you have already lost"&lt;br&gt;
Publikumsfrage: Was ist der Unterschied zwischen Hummingbird und Renovate? -- Beide lösen das gleiche Problem, Hummingbird benutzt Renovate im Hintergrund.&lt;/p&gt;

&lt;p&gt;10:15 bpftrace Viktor@RedHat &lt;br&gt;
Ich habe mich spontan daran erinnert, warum ich BPF-Trace verworfen habe. Es hat ein paar sehr nischige High-Level-Anwendungsfälle. Der Maintainer hat das Acknowledged und kündigt Erweiterungen an:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Macros&lt;/li&gt;
&lt;li&gt;Libraries and Imports&lt;/li&gt;
&lt;li&gt;C-Interoperation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;11:00 HTTP3 und Quick Evolution&lt;br&gt;
&lt;a href="https://www.goodreads.com/quotes/215637-simplicity-is-a-great-virtue-but-it-requires-hard-work" rel="noopener noreferrer"&gt;https://www.goodreads.com/quotes/215637-simplicity-is-a-great-virtue-but-it-requires-hard-work&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devconf</category>
      <category>conference</category>
      <category>it</category>
    </item>
    <item>
      <title>OpenVox rootless Alpine Containers</title>
      <dc:creator>Robert Waffen</dc:creator>
      <pubDate>Fri, 12 Jun 2026 13:42:44 +0000</pubDate>
      <link>https://dev.to/betadots/openvox-rootless-alpine-containers-1839</link>
      <guid>https://dev.to/betadots/openvox-rootless-alpine-containers-1839</guid>
      <description>&lt;p&gt;Over the last weeks, I spent a lot of my Open Source time at &lt;a href="https://www.betadots.de" rel="noopener noreferrer"&gt;betadots GmbH&lt;/a&gt; working on improving the OpenVox containers.&lt;/p&gt;

&lt;p&gt;The result: both &lt;code&gt;openvoxserver&lt;/code&gt; and &lt;code&gt;openvoxdb&lt;/code&gt; are now regularly published as &lt;strong&gt;Ubuntu&lt;/strong&gt; and &lt;strong&gt;Alpine&lt;/strong&gt; based container images 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  The PRs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/OpenVoxProject/container-openvoxdb/pull/88" rel="noopener noreferrer"&gt;https://github.com/OpenVoxProject/container-openvoxdb/pull/88&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/OpenVoxProject/container-openvoxserver/pull/131" rel="noopener noreferrer"&gt;https://github.com/OpenVoxProject/container-openvoxserver/pull/131&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may sound like a small packaging change at first glance, but it actually opens the door for several different deployment styles and operational preferences.&lt;/p&gt;

&lt;p&gt;Some users prefer Ubuntu based images for compatibility, familiarity and enterprise environments. Others want the smaller Alpine footprint, faster pulls and a more minimal runtime environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I got there
&lt;/h2&gt;

&lt;p&gt;Building the Alpine containers was a significant challenge because we don’t have packages here. Building Alpine packages would have been another nightmare to solve. So, I looked into the build scripts for the Ubuntu/RHEL packages and extracted all the necessary steps to get the server running. Ideally, I thought, "That’s only a JAR, how complicated can this be? Containers are made for running JARs!" But I couldn’t have been further from the truth. The OpenVox JARs are not self-running and depend on a lot of files and directories being in the right place. Getting this working was a bit of a hassle! But in the end, I got all the steps together, and the containers were building and even starting! They were working! Then, I had to do it rootless. So, we figured out every place where a chmod or chown is made and got this out of the starting process. It wasn’t easy! But we solved it in the end. With much help from the community!&lt;/p&gt;

&lt;h2&gt;
  
  
  Rootless containers in detail 🥷🐳
&lt;/h2&gt;

&lt;p&gt;One particularly exciting part: the Alpine images are already fully rootless.&lt;/p&gt;

&lt;p&gt;That means they are prepared to run without root privileges inside the container, improving the security posture and aligning better with modern container best practices.&lt;/p&gt;

&lt;p&gt;The Ubuntu variants are expected to follow soon as the remaining changes are being worked on.&lt;/p&gt;

&lt;p&gt;Rootless containers are one of those things that feel a bit like upgrading from telnet to SSH. Once the ecosystem is ready for it, going back feels strange.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community powered Open Source ❤️
&lt;/h2&gt;

&lt;p&gt;Even though I implemented most of the changes during my Open Source time at &lt;a href="https://www.betadots.de" rel="noopener noreferrer"&gt;betadots GmbH&lt;/a&gt;, this was absolutely not a solo effort.&lt;/p&gt;

&lt;p&gt;There were reviews, discussions, testing, feedback and many tiny details coming from the OpenVox community. Open Source thrives when individuals collectively contribute their ideas, refining them until the final product surpasses the original draft.&lt;/p&gt;

&lt;p&gt;Huge thanks to everyone involved 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Infrastructure projects are often built from many small building blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;container images&lt;/li&gt;
&lt;li&gt;CI pipelines&lt;/li&gt;
&lt;li&gt;packaging&lt;/li&gt;
&lt;li&gt;runtime security&lt;/li&gt;
&lt;li&gt;multi-distro support&lt;/li&gt;
&lt;li&gt;rootless compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually, these changes may seem minor.&lt;/p&gt;

&lt;p&gt;But together they make the ecosystem more flexible, more secure and easier to integrate into different environments, from homelabs to enterprise deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;There is still more work ahead around rootless support, image hardening and ecosystem improvements, but this is another nice milestone for OpenVox containers.&lt;/p&gt;

&lt;p&gt;Feedback, testing and contributions are always welcome.&lt;/p&gt;

&lt;p&gt;Open Source is still one of the few places where random people on the internet collectively assemble giant infrastructures out of YAML, shell scripts and sleep deprivation 🤖✨&lt;/p&gt;

&lt;p&gt;Cheers, Robert&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>containers</category>
      <category>openvox</category>
    </item>
    <item>
      <title>puppet-checkmk dusted off 🧹🦊</title>
      <dc:creator>Marcus</dc:creator>
      <pubDate>Fri, 12 Jun 2026 08:47:43 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-checkmk-dusted-off-1jc</link>
      <guid>https://dev.to/betadots/puppet-checkmk-dusted-off-1jc</guid>
      <description>&lt;p&gt;VoxPupuli's puppet-checkmk &lt;a href="https://github.com/voxpupuli/puppet-check_mk" rel="noopener noreferrer"&gt;[1]&lt;/a&gt;&lt;a href="https://forge.puppet.com/modules/puppet/check_mk/readme" rel="noopener noreferrer"&gt;[2]&lt;/a&gt; had become invisible on the Puppet Forge. &lt;a href="https://forge.puppet.com/modules?q=check_mk" rel="noopener noreferrer"&gt;[3]&lt;/a&gt; The Forge has a simple filter: No updates for a long time? Hide it!&lt;/p&gt;

&lt;p&gt;Since we hadn't put out a release in a while, we got dropped off the Forge. We've fixed that: dusted off the metadata and added modern operating systems (RedHat-10, Debian-13) to the test matrix.&lt;/p&gt;

&lt;p&gt;As sysadmins may guess, there were some quirks with EPEL. I am looking at you, Oracle Linux! ;-) &lt;a href="https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13" rel="noopener noreferrer"&gt;[4]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://github.com/voxpupuli/puppet-check_mk" rel="noopener noreferrer"&gt;https://github.com/voxpupuli/puppet-check_mk&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://forge.puppet.com/modules/puppet/check_mk/readme" rel="noopener noreferrer"&gt;https://forge.puppet.com/modules/puppet/check_mk/readme&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://forge.puppet.com/modules?q=check_mk" rel="noopener noreferrer"&gt;https://forge.puppet.com/modules?q=check_mk&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13" rel="noopener noreferrer"&gt;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&lt;/a&gt;&lt;/p&gt;

</description>
      <category>voxpupuli</category>
      <category>checkmk</category>
    </item>
    <item>
      <title>puppet-checkmk entstaubt 🧹🦊</title>
      <dc:creator>Marcus</dc:creator>
      <pubDate>Fri, 12 Jun 2026 08:47:42 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-checkmk-entstaubt-5a61</link>
      <guid>https://dev.to/betadots/puppet-checkmk-entstaubt-5a61</guid>
      <description>&lt;p&gt;VoxPupulis puppet-checkmk &lt;a href="https://github.com/voxpupuli/puppet-check_mk" rel="noopener noreferrer"&gt;[1]&lt;/a&gt;&lt;a href="https://forge.puppet.com/modules/puppet/check_mk/readme" rel="noopener noreferrer"&gt;[2]&lt;/a&gt; war in der Puppet-Forge unsichtbar geworden. &lt;a href="https://forge.puppet.com/modules?q=check_mk" rel="noopener noreferrer"&gt;[3]&lt;/a&gt;&lt;br&gt;
Die Forge hat einen schlichten Filter: &amp;gt;FIXNUM Zeit kein Update erhalten? Ausblenden!&lt;/p&gt;

&lt;p&gt;Wei wir schon lange kein Release mehr veröffentlicht haben, sind wir aus der Forge gefallen. Wir haben das behoben: Metadaten entstaubt und moderne Betriebssysteme (RedHat-10, Debian-13) in die Test-Matrix eingefügt.&lt;/p&gt;

&lt;p&gt;In guter Sysadmin-Gewohnheit gab es Freude mit EPEL. Du bist gemeint, Oracle-Linux! ;-) &lt;a href="https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13" rel="noopener noreferrer"&gt;[4]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://github.com/voxpupuli/puppet-check_mk" rel="noopener noreferrer"&gt;https://github.com/voxpupuli/puppet-check_mk&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://forge.puppet.com/modules/puppet/check_mk/readme" rel="noopener noreferrer"&gt;https://forge.puppet.com/modules/puppet/check_mk/readme&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://forge.puppet.com/modules?q=check_mk" rel="noopener noreferrer"&gt;https://forge.puppet.com/modules?q=check_mk&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13" rel="noopener noreferrer"&gt;https://github.com/voxpupuli/puppet-nginx/blob/master/spec/spec_helper_acceptance.rb#L5-L13&lt;/a&gt;&lt;/p&gt;

</description>
      <category>voxpupuli</category>
      <category>checkmk</category>
    </item>
    <item>
      <title>Puppet Enterprise - Neue Releases und Upgrade Zyklen</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Wed, 08 Apr 2026 13:59:44 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-enterprise-neue-releases-und-upgrade-zyklen-i13</link>
      <guid>https://dev.to/betadots/puppet-enterprise-neue-releases-und-upgrade-zyklen-i13</guid>
      <description>&lt;p&gt;Bisher gab es bei Puppet Enterprise 2 unterschiedliche Support Releases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;LTS - Long Term Support und&lt;/li&gt;
&lt;li&gt;STS - Standard (oder Short) Term Support&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Die LTS Releases hatten üblicherweise 2 bis 3 Jahre Support, bevor man ein Upgrade durchführen musste.&lt;br&gt;
Bei STS Releases bezog sich der Support nur auf das jeweils letzte Release.&lt;/p&gt;

&lt;p&gt;Mit Puppet Enterprise 2026 (&lt;a href="https://www.puppet.com/products/puppet-enterprise/support-lifecycle" rel="noopener noreferrer"&gt;Release ist angekündigt für August 2026&lt;/a&gt;) hat &lt;a href="https://perforce.com" rel="noopener noreferrer"&gt;Perforce&lt;/a&gt; die Support Matrix überarbeitet.&lt;br&gt;
Der neue Support Zyklus wird als &lt;code&gt;Latest / Latest-1&lt;/code&gt; bezeichnet.&lt;/p&gt;

&lt;p&gt;In diesem Posting werden die wesentlichen Unterschiede dargestellt und was man in Zukunft bei der Nutzung von Puppet Enterprise berücksichtigen sollte.&lt;/p&gt;




&lt;h2&gt;
  
  
  Latest
&lt;/h2&gt;

&lt;p&gt;Mit &lt;code&gt;Latest&lt;/code&gt; wird das jeweils aktuelle Puppet Enterprise &lt;strong&gt;Major&lt;/strong&gt; Release bezeichnet.&lt;br&gt;
Aktuell ist dies das letzte Release von PE 2025.&lt;/p&gt;

&lt;p&gt;Mit dem Release von PE 2026, wird PE 2026 das &lt;code&gt;Latest&lt;/code&gt; Support Release.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Latest&lt;/code&gt; Releases haben einen vollständigen Support über einen Zeitraum von 12 Monaten.&lt;br&gt;
Innerhalb dieses Zeitraumes sind neue Features und Verbesserungen, Sicherheitsupdates und Fehlerkorrekturen im Support verfügbar.&lt;/p&gt;

&lt;p&gt;Danach gibt es in der Folge Minor Releases, die vierteljährlich zur Verfügung gestellt werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latest-1
&lt;/h2&gt;

&lt;p&gt;Als &lt;code&gt;Latest-1&lt;/code&gt; wird das jeweilige vorherige Release bezeichnet, nachdem eine neue Major Version veröffentlicht wurde.&lt;/p&gt;

&lt;p&gt;Wenn PE 2026 released wird, wird das PE 2025 Release zu &lt;code&gt;Latest-1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Latest-1&lt;/code&gt; Releases haben einen eingeschränkten Support über einen Zeitraum von 12 Monaten.&lt;br&gt;
Innerhalb dieses Zeitraumes sind nur Sicherheitsupdates und Fehlerkorrekturen im Support verfügbar.&lt;/p&gt;

&lt;h2&gt;
  
  
  aktuelles LTS Releases
&lt;/h2&gt;

&lt;p&gt;Das aktuelle LTS Release von Puppet Enterprise ist die Version 2023.8.&lt;br&gt;
Mit dem Release von PE 2026 wird diese Version nicht mehr unterstützt.&lt;/p&gt;

&lt;p&gt;Alle anderen älteren Puppet Enterprise Releases sind nicht mehr im Support enthalten.&lt;br&gt;
Wenn eine ältere Puppet Enterprise Version als PE 2025 im Einsatz ist, sollte unbedingt ein Upgrade geplant und durchgeführt werden. Welche Upgrades möglich sind, ist in der &lt;a href="https://help.puppet.com/pe/current/topics/supported_upgrade_paths.htm" rel="noopener noreferrer"&gt;Dokumentation zu Puppet Enterprise Upgrades&lt;/a&gt; hinterlegt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Releases und Support Zeitraum
&lt;/h2&gt;

&lt;p&gt;Stand 08.04.2026&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PE Version&lt;/th&gt;
&lt;th&gt;Support Beginn&lt;/th&gt;
&lt;th&gt;Support Name&lt;/th&gt;
&lt;th&gt;Support Umfang&lt;/th&gt;
&lt;th&gt;Support Ende&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2023.8.x&lt;/td&gt;
&lt;td&gt;&amp;nbsp;aktuell&lt;/td&gt;
&lt;td&gt;LTS&lt;/td&gt;
&lt;td&gt;Vollständiger Support&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023.8.x&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;td&gt;LTS&lt;/td&gt;
&lt;td&gt;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;02/2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;2025.x&lt;/td&gt;
&lt;td&gt;aktuell&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;Vollständiger Support&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025.x&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;td&gt;Latest-1&lt;/td&gt;
&lt;td&gt;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;08/2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026.x&lt;/td&gt;
&lt;td&gt;08/2026&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;&amp;nbsp;Vollständiger Support&lt;/td&gt;
&lt;td&gt;??/2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026.x&lt;/td&gt;
&lt;td&gt;??/2027&lt;/td&gt;
&lt;td&gt;Latest-1&lt;/td&gt;
&lt;td&gt;&amp;nbsp;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;2027.x&lt;/td&gt;
&lt;td&gt;??/2027&lt;/td&gt;
&lt;td&gt;Latest&lt;/td&gt;
&lt;td&gt;Vollständiger Support&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2027.x&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;td&gt;Latest-1&lt;/td&gt;
&lt;td&gt;eingeschränkter Support&lt;/td&gt;
&lt;td&gt;??/2029&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2028.x&lt;/td&gt;
&lt;td&gt;??/2028&lt;/td&gt;
&lt;td&gt;&amp;nbsp;Latest&lt;/td&gt;
&lt;td&gt;&amp;nbsp;Vollständiger Support&lt;/td&gt;
&lt;td&gt;&amp;nbsp;??/2029&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Perforce entwirrt und standardisiert die Support Zyklen.&lt;br&gt;
Mit den jährliche Major Releases und definierten Zeiträumen für den eingeschränkten Support können Kunden innerhalb von 24 Monaten ein Update durchführen, ohne den Support zu verlieren.&lt;/p&gt;

&lt;p&gt;Bei der Vorbereitung und Durchführung von Updates unterstützen wir unsere Kunden gern.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>puppet</category>
      <category>news</category>
    </item>
    <item>
      <title>How to keep your Puppet modules up to date with Renovate</title>
      <dc:creator>Robert Waffen</dc:creator>
      <pubDate>Wed, 25 Mar 2026 16:14:50 +0000</pubDate>
      <link>https://dev.to/betadots/how-to-keep-your-puppet-modules-up-to-date-with-renovate-19jn</link>
      <guid>https://dev.to/betadots/how-to-keep-your-puppet-modules-up-to-date-with-renovate-19jn</guid>
      <description>&lt;p&gt;Managing a Puppet control repository with a growing list of dependencies can quickly become a challenge.&lt;br&gt;
Keeping everything up to date manually?&lt;br&gt;
That’s not just tedious — it’s also error-prone.&lt;/p&gt;

&lt;p&gt;Modules are updated frequently, sometimes daily.&lt;br&gt;
With a long list of dependencies, it’s easy to miss important updates.&lt;br&gt;
And let’s be honest: humans are not exactly known for perfect consistency when it comes to repetitive tasks.&lt;/p&gt;

&lt;p&gt;So why not let a bot handle it?&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Renovate?
&lt;/h2&gt;

&lt;p&gt;Renovate is designed to take over exactly this kind of work.&lt;br&gt;
It continuously scans your repositories, detects dependency updates, and automatically creates merge requests for you — including changelogs.&lt;/p&gt;

&lt;p&gt;Instead of manually checking versions, you simply review and merge PRs.&lt;br&gt;
Much cleaner.&lt;br&gt;
Much safer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Assumptions
&lt;/h2&gt;

&lt;p&gt;Before we get started, this setup assumes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your repositories are hosted on GitLab&lt;/li&gt;
&lt;li&gt;You are using GitLab CI with runners&lt;/li&gt;
&lt;li&gt;You can run container-based jobs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setting Up Renovate
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a Renovate Runner Repository
&lt;/h3&gt;

&lt;p&gt;Create a dedicated repository in GitLab, for example: renovate-runner&lt;/p&gt;

&lt;p&gt;Inside this repository, create a &lt;code&gt;config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;autodiscover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dependencyDashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// We only enable the puppet manager here&lt;/span&gt;
  &lt;span class="c1"&gt;// see all managers: https://docs.renovatebot.com/modules/manager/&lt;/span&gt;
  &lt;span class="na"&gt;enabledManagers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// GitLab API - update with your instance URL&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://gitlab.example.com/api/v4/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config:base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:semanticCommits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:semanticCommitTypeAll(chore)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renovate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gitlab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;prCreation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;immediate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;prHourlyLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppet/control-repo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;requireConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;reviewers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rwaffen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RENOVATE_TOKEN&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration will create one merge request per dependency update.&lt;/p&gt;

&lt;p&gt;If you prefer grouped updates, you can extend it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;packageRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;matchManagers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;puppet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;groupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{{manager}}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add GitLab CI Configuration
&lt;/h3&gt;

&lt;p&gt;Create a .gitlab-ci.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;run_renovate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/voxpupuli/renovate:latest&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;resource_group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;renovate $RENOVATE_EXTRA_FLAGS&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;schedules&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;triggers&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
  &lt;span class="c1"&gt;# variables:&lt;/span&gt;
  &lt;span class="c1"&gt;#   LOG_LEVEL: debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Renovate
&lt;/h2&gt;

&lt;p&gt;With everything in place, you can trigger a pipeline in your &lt;code&gt;renovate-runner&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;Once started, Renovate will take over and handle the full update cycle for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It launches the Renovate container&lt;/li&gt;
&lt;li&gt;Authenticates using your configured token&lt;/li&gt;
&lt;li&gt;Scans the defined repositories&lt;/li&gt;
&lt;li&gt;Detects dependency files such as &lt;code&gt;Puppetfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resolves current versions and checks for new releases or tags&lt;/li&gt;
&lt;li&gt;Creates merge requests whenever updates are available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, your role shifts from &lt;em&gt;maintainer&lt;/em&gt; to &lt;em&gt;reviewer&lt;/em&gt;:&lt;br&gt;
you simply go through the generated merge requests and decide what to merge.&lt;/p&gt;

&lt;p&gt;To make this process truly hands-off, you should also configure a scheduled pipeline in GitLab.&lt;br&gt;
This ensures Renovate runs regularly and keeps your dependencies continuously up to date — without any manual triggering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication: &lt;code&gt;RENOVATE_TOKEN&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Renovate requires a Personal Access Token (&lt;code&gt;RENOVATE_TOKEN&lt;/code&gt;) to interact with your repositories.&lt;/p&gt;

&lt;p&gt;Required permissions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;api&lt;/li&gt;
&lt;li&gt;write_repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is highly recommended to create this as a group access token, so Renovate can access all relevant repositories — especially important if your Puppetfile includes Git-based modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Container Image
&lt;/h2&gt;

&lt;p&gt;Renovate provides official container images, but they come with some trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large size&lt;/li&gt;
&lt;li&gt;Based on Ubuntu&lt;/li&gt;
&lt;li&gt;Higher number of known vulnerabilities (CVEs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vox Pupuli provides a leaner alternative:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller footprint&lt;/li&gt;
&lt;li&gt;Based on Alpine&lt;/li&gt;
&lt;li&gt;Significantly fewer vulnerabilities
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMAGE                                 ID             DISK USAGE   CONTENT SIZE   EXTRA
ghcr.io/renovatebot/renovate:latest   0334065a0093       1.87GB          416MB
ghcr.io/voxpupuli/renovate:latest     e354b2af781c        726MB          132MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vulnerability Scan (grype)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grype ghcr.io/renovatebot/renovate:latest

 ✔ Scanned for vulnerabilities     [297 vulnerability matches]
   ├── by severity: 1 critical, 10 high, 971 medium, 137 low, 15 negligible
   └── by status:   14 fixed, 1120 not-fixed, 837 ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grype ghcr.io/voxpupuli/renovate:latest

 ✔ Scanned for vulnerabilities     [19 vulnerability matches]
   ├── by severity: 0 critical, 15 high, 4 medium, 0 low, 0 negligible
   └── by status:   13 fixed, 6 not-fixed, 0 ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;With Renovate in place, dependency management becomes predictable and automated.&lt;br&gt;
Instead of chasing updates manually, you get a steady stream of structured merge requests — complete with context and changelogs.&lt;br&gt;
In other words: less chaos, more control.&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>renovate</category>
      <category>voxpupuli</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>🧩 Puppet Module Update Process</title>
      <dc:creator>Robert Waffen</dc:creator>
      <pubDate>Fri, 11 Jul 2025 07:59:30 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-module-update-process-3f54</link>
      <guid>https://dev.to/betadots/puppet-module-update-process-3f54</guid>
      <description>&lt;h2&gt;
  
  
  🔍 1. Identify and Analyze the Module
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Extract a module name from the &lt;code&gt;Puppetfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search for the module on &lt;a href="https://forge.puppet.com" rel="noopener noreferrer"&gt;Puppet Forge&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Compare available versions and identify the latest one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 2. Review Changes in the Module
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Follow the &lt;strong&gt;Project URL&lt;/strong&gt; on the Forge page to the GitHub repository.&lt;/li&gt;
&lt;li&gt;Check recent changes under &lt;strong&gt;Releases&lt;/strong&gt; or in the &lt;strong&gt;Changelog&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://forge.puppet.com/modules/puppet/systemd/readme" rel="noopener noreferrer"&gt;Forge: puppet/systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/voxpupuli/puppet-systemd/releases" rel="noopener noreferrer"&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/voxpupuli/puppet-systemd/blob/master/CHANGELOG.md" rel="noopener noreferrer"&gt;GitHub Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ 3. Watch for Breaking Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Possible breaking changes:

&lt;ul&gt;
&lt;li&gt;Removal of support for EOL software&lt;/li&gt;
&lt;li&gt;API changes&lt;/li&gt;
&lt;li&gt;Renamed parameters or variables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Note: Not every breaking change will affect your setup.

&lt;ul&gt;
&lt;li&gt;Example: Dropping EL6 support likely doesn’t concern you.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 4. Evaluate Other Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Linked pull requests (PRs) may offer additional insights.&lt;/li&gt;
&lt;li&gt;Check whether changes are understandable and whether breaking changes apply to your environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌱 5. Integrate into Development Branches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If the module is considered ready for update:

&lt;ul&gt;
&lt;li&gt;Integrate into a &lt;strong&gt;feature&lt;/strong&gt; or &lt;strong&gt;development&lt;/strong&gt; branch.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌲 6. Control Repo Branch Structure
&lt;/h2&gt;

&lt;p&gt;Typically present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally: &lt;code&gt;staging&lt;/code&gt; before &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Additionally: 0–n &lt;strong&gt;feature branches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 7. Testing in Development Environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test the updated module in a suitable environment (VM, container).&lt;/li&gt;
&lt;li&gt;Observe how it interacts with other modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Possible findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependencies on specific module versions&lt;/li&gt;
&lt;li&gt;New facts writing additional data to PuppetDB&lt;/li&gt;
&lt;li&gt;New or modified parameters requiring Hiera data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 8. Forward the Change
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Once all adjustments are complete:

&lt;ul&gt;
&lt;li&gt;Pass the change to the next branch for further testing or rollout preparation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤖 9. Automation with Renovate Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This process is quite detailed and time-consuming.&lt;/li&gt;
&lt;li&gt;With &lt;a href="https://www.mend.io/renovate/" rel="noopener noreferrer"&gt;Renovate Bot&lt;/a&gt;, collecting and reviewing relevant updates becomes much easier.&lt;/li&gt;
&lt;li&gt;Renovate can be integrated into GitLab or GitHub.&lt;/li&gt;
&lt;li&gt;It works similarly to GitHub’s built-in &lt;a href="https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt;, but is more flexible and configurable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧰 10. Automation with VoxBox
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can also try using the Vox Pupuli VoxBox to list all dependencies for the entire control repo.

&lt;ul&gt;
&lt;li&gt;This doesn't work with every setup.&lt;/li&gt;
&lt;li&gt;If there are private modules in the Puppetfile, the person running the command must have access to them.&lt;/li&gt;
&lt;li&gt;Example command:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman run -it --rm -v $PWD:/repo:Z ghcr.io/voxpupuli/voxbox:latest r10k:dependencies&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;More useful information can be found in the &lt;a href="https://github.com/voxpupuli/container-voxbox" rel="noopener noreferrer"&gt;Vox Pupuli VoxBox documentation&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>puppet</category>
      <category>devops</category>
    </item>
    <item>
      <title>🧩 Puppet Modul Update Prozess</title>
      <dc:creator>Robert Waffen</dc:creator>
      <pubDate>Fri, 11 Jul 2025 07:58:40 +0000</pubDate>
      <link>https://dev.to/betadots/puppet-modul-update-prozess-3mek</link>
      <guid>https://dev.to/betadots/puppet-modul-update-prozess-3mek</guid>
      <description>&lt;h2&gt;
  
  
  🔍 1. Modul identifizieren und analysieren
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Einen Modulnamen aus dem &lt;code&gt;Puppetfile&lt;/code&gt; entnehmen.&lt;/li&gt;
&lt;li&gt;In der &lt;a href="https://forge.puppet.com" rel="noopener noreferrer"&gt;Puppet Forge&lt;/a&gt; nach dem Modul suchen.&lt;/li&gt;
&lt;li&gt;Verfügbare Versionen vergleichen und die neueste identifizieren.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 2. Änderungen im Modul prüfen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Über den Link zur &lt;strong&gt;Project URL&lt;/strong&gt; auf der Forge-Seite zum GitHub-Repository wechseln.&lt;/li&gt;
&lt;li&gt;Dort unter &lt;strong&gt;Releases&lt;/strong&gt; oder im &lt;strong&gt;Changelog&lt;/strong&gt; die letzten Änderungen prüfen:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beispiel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://forge.puppet.com/modules/puppet/systemd/readme" rel="noopener noreferrer"&gt;Forge: puppet/systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/voxpupuli/puppet-systemd/releases" rel="noopener noreferrer"&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/voxpupuli/puppet-systemd/blob/master/CHANGELOG.md" rel="noopener noreferrer"&gt;GitHub Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ 3. Auf Breaking Changes achten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mögliche Breaking Changes:

&lt;ul&gt;
&lt;li&gt;Entfernung der Unterstützung für EOL-Software&lt;/li&gt;
&lt;li&gt;Änderungen an der API&lt;/li&gt;
&lt;li&gt;Umbenennung von Variablen&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Hinweis: Nicht jede Breaking Change ist für jedes Setup relevant.

&lt;ul&gt;
&lt;li&gt;Beispiel: Der Entfall von EL6-Support betrifft euch vermutlich nicht.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 4. Sonstige Änderungen bewerten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Auch verlinkte Pull Requests (PRs) können Aufschluss geben.&lt;/li&gt;
&lt;li&gt;Prüfen, ob Änderungen nachvollziehbar sind und keine Breaking Changes enthalten, die euch betreffen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌱 5. Aufnahme in Entwicklungszweige
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wenn das Modul als updatefähig eingeschätzt wird:

&lt;ul&gt;
&lt;li&gt;Aufnahme in einen &lt;strong&gt;Feature-&lt;/strong&gt; oder &lt;strong&gt;Development-Branch&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌲 6. Branch-Struktur im Control-Repo
&lt;/h2&gt;

&lt;p&gt;Typischerweise vorhanden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Zusätzlich: 0–n &lt;strong&gt;Feature-Branches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 7. Tests in Entwicklungsumgebung
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test des neuen Moduls in geeigneter Umgebung (VM, Container)&lt;/li&gt;
&lt;li&gt;Beobachtung des Zusammenspiels mit anderen Modulen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mögliche Erkenntnisse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abhängigkeit zu anderen Modul-Versionen&lt;/li&gt;
&lt;li&gt;Neue Facts, die zusätzliche Daten in die PuppetDB schreiben&lt;/li&gt;
&lt;li&gt;Neue oder geänderte Parameter, die Hiera-Daten erfordern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 8. Change weiterreichen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wenn alle Anpassungen erledigt sind:

&lt;ul&gt;
&lt;li&gt;Übergabe des Changes in den nächsten Branch zur weiteren Prüfung oder Vorbereitung für den Rollout.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤖 9. Automatisierung mit Renovate Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dieser Prozess ist sehr kleinteilig und kann zeitaufwendig sein.&lt;/li&gt;
&lt;li&gt;Mit Hilfe von &lt;a href="https://www.mend.io/renovate/" rel="noopener noreferrer"&gt;Renovate Bot&lt;/a&gt; lässt sich das Sichten und Sammeln der relevanten Informationen deutlich erleichtern.&lt;/li&gt;
&lt;li&gt;Renovate ist ein Bot, der sich in GitLab oder GitHub integrieren lässt.&lt;/li&gt;
&lt;li&gt;Er funktioniert ähnlich wie der GitHub-eigene &lt;a href="https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt;, ist aber deutlich flexibler und konfigurierbarer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧰 10. Automatisierung mit VoxBox
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Man kann auch versuchen mit der Vox Pupuli Voxbox die Abhängigkeiten für das gesamte Control-Repo anzeigen zu lassen.

&lt;ul&gt;
&lt;li&gt;Das klappt nicht bei jedem Setup.&lt;/li&gt;
&lt;li&gt;Wenn im Puppetfile private Module sind, muss der jenige der es ausführt, Zugriff auf diese haben.&lt;/li&gt;
&lt;li&gt;Beispielbefehl:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman run -it --rm -v $PWD:/repo:Z ghcr.io/voxpupuli/voxbox:latest r10k:dependencies&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Weitere nützliche Informationen gibt es in der &lt;a href="https://github.com/voxpupuli/container-voxbox" rel="noopener noreferrer"&gt;Vox Pupuli VoxBox Dokumentation&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>puppet</category>
      <category>devops</category>
    </item>
    <item>
      <title>Das neue Puppet Open Source Projekt heißt "OpenVox"</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Tue, 28 Jan 2025 15:00:38 +0000</pubDate>
      <link>https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330</link>
      <guid>https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330</guid>
      <description>&lt;p&gt;&lt;a href="https://puppet.com" rel="noopener noreferrer"&gt;Puppet by Perforce&lt;/a&gt; hat &lt;a href="https://www.puppet.com/blog/open-source-puppet-updates-2025" rel="noopener noreferrer"&gt;angekündigt&lt;/a&gt;,&lt;br&gt;
dass die Open Source Pakete ab 2025 nur noch nach Zustimmung einer nicht weiter definierten EULA verfügbar gemacht werden sollen. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;In early 2025, Puppet will begin to ship any new binaries and packages developed by our team to a private, hardened, and controlled location. Community contributors will have free access to this private repo under the terms of an End-User License Agreement (EULA) for development use.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Dabei muss berücksichtigt werden, dass die kostenfreie Variante nur für maximal 25 Nodes gilt.&lt;br&gt;
Wer mehr Systeme mit Puppet Open Source verwalten will, benötigt eine &lt;code&gt;Puppet Labs Support Commercial License&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Die Open Source Community hat das Gespräch mit dem Puppet Management gesucht.&lt;/p&gt;

&lt;p&gt;Hier das Resultat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Das neue Puppet Open Source Projekt heißt &lt;code&gt;OpenVox&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Vorbereitung&lt;/li&gt;
&lt;li&gt;Aus Puppet wird OpenVox und Puppet Open Source&lt;/li&gt;
&lt;li&gt;Source Code, Pakete und Container&lt;/li&gt;
&lt;li&gt;Ich nutze Puppet Open Source. Was muss ich tun?&lt;/li&gt;
&lt;li&gt;Puppet Open Source&lt;/li&gt;
&lt;li&gt;OpenVox&lt;/li&gt;
&lt;li&gt;Die weitere Entwicklung&lt;/li&gt;
&lt;li&gt;Wie kann man helfen?&lt;/li&gt;
&lt;li&gt;Professioneller Support&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Vorbereitung
&lt;/h2&gt;

&lt;p&gt;Vor dem ersten Gespräch mit Puppet hat sich die Community im Vorfeld besprochen und analysiert, ob man als Open Source Projekt in der Lage sein kann, die weitere Entwicklung zu stemmen.&lt;br&gt;
Das Feedback aus der Open Source Community und von Open Source Usern war sehr positiv.&lt;/p&gt;

&lt;p&gt;Einige Nutzer haben sogar direkt angekündigt, dass man Entwickler für dedizierte Aufgaben zur Verfügung stellen möchte.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aus Puppet wird OpenVox und Puppet Open Source
&lt;/h2&gt;

&lt;p&gt;Die Open Source Community hat sich mit Puppet by Perforce darauf geeinigt, dass die weitere Entwicklung von Puppet in einem Fork unter der Verwaltung der Open Source Community erfolgen soll.&lt;/p&gt;

&lt;p&gt;Nach einer &lt;a href="https://github.com/OpenVoxProject/planning/discussions/9" rel="noopener noreferrer"&gt;öffentlichen Abstimmung Ende 2024&lt;/a&gt; innerhalb der Community, hat man sich für den Namen &lt;code&gt;OpenVox&lt;/code&gt; entschieden und beschlossen, dass die Entwicklung innerhalb der bereits existierenden &lt;a href="https://voxpupuli.org" rel="noopener noreferrer"&gt;&lt;code&gt;Vox Pupuli&lt;/code&gt; Puppet Open Source Community&lt;/a&gt; auf &lt;a href="https://github.com/voxpupuli" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; stattfinden soll.&lt;/p&gt;

&lt;p&gt;Da Perforce die Namensrechte an &lt;code&gt;Puppet&lt;/code&gt; besitzt, hat man gemeinsam mit Perforce entschieden, dass die Pakete, die durch das OpenVox Projekt erstellt werden, nicht mehr &lt;code&gt;Puppet&lt;/code&gt; im Namen haben sollen.&lt;br&gt;
Dies ist auch in Hinsicht auf Differenzierbarkeit der Herkunft der Pakete sinnvoll.&lt;br&gt;
Die Open Source Pakete von Perforce werden weiterhin &lt;code&gt;puppet-agent&lt;/code&gt;, &lt;code&gt;puppetdb&lt;/code&gt; und &lt;code&gt;puppetserver&lt;/code&gt; heißen.&lt;/p&gt;

&lt;p&gt;Gemeinsam mit Perforce wurde vereinbart, dass man sowohl die Kommandozeile, wie auch Namen der Konfigurationsdateien und die Schnittstellen zur Erweiterung von Puppet ( &lt;code&gt;/lib/puppet&lt;/code&gt; in Modulen) zu existierenden Installation und Code kompatibel halten möchte.&lt;/p&gt;

&lt;p&gt;Die weitere Kompatibilität soll durch ein &lt;code&gt;Language Steering Committee&lt;/code&gt; sichergestellt werden, in dem alle Beteiligten gemeinsam über Änderungen an der Puppet DSL oder Inkompatibilitäten entscheiden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code, Pakete und Container
&lt;/h2&gt;

&lt;p&gt;Der frei verfügbare &lt;a href="https://github.com/puppetlabs" rel="noopener noreferrer"&gt;Puppet Open Source Code&lt;/a&gt; wurde in das &lt;a href="https://github.com/openvoxproject" rel="noopener noreferrer"&gt;OpenVox GitHub Projekt&lt;/a&gt; geforkt.&lt;/p&gt;

&lt;p&gt;Im ersten Schritt hat das Re-branding stattgefunden.&lt;br&gt;
&lt;strong&gt;Für Anwender ist es wichtig zu wissen, dass Kommandozeilen Tools, Bibliotheken und Puppet Erweiterungen auch unter OpenVox vollständig kompatibel zu Puppet Open Source sind.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aktuell werden die Pipelines zur Erstellung der RedHat und Debian Pakete auf öffentliche GitHub Actions umgestellt und sollen sehr bald zur Verfügung gestellt werden.&lt;br&gt;
Erste reproduzierbare Build wurden bereits getetstet.&lt;br&gt;
Pakete für Windows und MacOS sind ebenfalls geplant.&lt;/p&gt;

&lt;p&gt;Die Puppet Container (&lt;a href="https://github.com/voxpupuli/container-puppetserver" rel="noopener noreferrer"&gt;puppetserver&lt;/a&gt; und &lt;a href="https://github.com/voxpupuli/container-puppetdb" rel="noopener noreferrer"&gt;puppetdb&lt;/a&gt;) werden nicht mehr weiter gepflegt!&lt;/p&gt;

&lt;p&gt;Statt dessen werden OpenVox Container zur Verfügung gestellt. Aktuell wird an &lt;a href="https://github.com/OpenVoxProject/container-openvoxserver" rel="noopener noreferrer"&gt;OpenVox Server&lt;/a&gt; und &lt;a href="https://github.com/OpenVoxProject/container-openvoxdb" rel="noopener noreferrer"&gt;OpenVoxDB&lt;/a&gt; gearbeitet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ich nutze Puppet Open Source. Was muss ich tun?
&lt;/h2&gt;

&lt;p&gt;Es gibt 2 Möglichkeiten:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Puppet Open Source&lt;/li&gt;
&lt;li&gt;OpenVox&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Puppet Open Source
&lt;/h3&gt;

&lt;p&gt;Man muss bei Puppet die EULA akzeptieren und sich ab 25 Nodes um eine Lizenz kümmern.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenVox
&lt;/h3&gt;

&lt;p&gt;Die Installations-Anleitung für die Migration findet man auf der&lt;br&gt;
&lt;a href="https://voxpupuli.org/openvox/install/" rel="noopener noreferrer"&gt;OpenVox Projekt Seite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Da die Konfigurationspfade und -dateien, sowie die Kommandos alle identisch sind, muss keine weitere Anpassung oder Umstellung vorgenommen werden.&lt;br&gt;
Auch der existierende Puppet Code funktioniert ohne Anpassungen.&lt;/p&gt;

&lt;p&gt;Nutzer von OpenVox werden gebeten die aktuellen Pakete und Container in einer Testumgebung zu validieren.&lt;/p&gt;

&lt;h2&gt;
  
  
  Die weitere Entwicklung
&lt;/h2&gt;

&lt;p&gt;Es gibt eine ganze Reihe von Wünschen aus der Community, die bisher leider nicht umgesetzt wurden.&lt;br&gt;
Zuerst möchte man aktuelle Betriebssystemversionen unterstützen. Hierbei hatte die Community oft nach Paketen für Debian stable und testing gefragt.&lt;/p&gt;

&lt;p&gt;Im nächsten Schritt soll der Code von alten Artefakten befreit werden.&lt;br&gt;
Nicht mehr gepflegte Ruby Erweiterungen sollen durch moderne Implementierungen ersetzt werden.&lt;br&gt;
Der Code Zoo (Jruby, Clojure, Java) soll aufgeräumt werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wie kann man helfen?
&lt;/h2&gt;

&lt;p&gt;Die Puppet Community möchte sich als Professionelles Open Source Projekt etablieren.&lt;br&gt;
Dazu sucht man die finanzielle Unterstützung von Usern, Firmen und öffentlichen Einrichtungen für Open Source Arbeit.&lt;/p&gt;

&lt;p&gt;Auch individuelle Zuarbeit ist gerne gesehen. Es muss Dokumentation geschrieben werden, die Pakete müssen getestet werden.&lt;br&gt;
Die Community braucht die Unterstützung der Anwender, sei es durch Zuarbeit, Code Reviews oder Bug Reports.&lt;/p&gt;

&lt;p&gt;Im nächsten Schritt benötigt man Spiegel Server, Bandbreite und Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Professioneller Support
&lt;/h2&gt;

&lt;p&gt;Open Source Support ist für viele Kunden ein wichtiger Aspekt bei der Wahl der Mittel.&lt;br&gt;
Für OpenVox gibt es bereits jetzt schon 2 Firmen, die Support anbieten:&lt;/p&gt;

&lt;p&gt;Wir von der &lt;a href="https://www.betadots.de" rel="noopener noreferrer"&gt;betadots GmbH&lt;/a&gt; unterstützen Kunden bei Planung, Installation, Updates von OpenVox, Puppet Open Source und Puppet Enterprise und kümmern uns um die Weiterbildung der Mitarbeiter im Umfeld von Git und GitLab, Foreman/Katello, Puppet/OpenVox und Linux HA.&lt;/p&gt;

&lt;p&gt;In Amerikanischem Raum wird die Firma &lt;a href="https://overlookinfratech.com/" rel="noopener noreferrer"&gt;overlookinfratech&lt;/a&gt; Ansprechpartner für OpenVox Support sein.&lt;/p&gt;

&lt;p&gt;Weitere Firmen werden auf der &lt;a href="https://voxpupuli.org/openvox/support/" rel="noopener noreferrer"&gt;Support Übersicht von OpenVox&lt;/a&gt; gelistet werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;OpenVox ist ein drop-in Ersatz für Puppet Open Source.&lt;/p&gt;

&lt;p&gt;Die Community hat gezeigt, dass sie den Wunsch nach Weiterentwicklung hat und in der Lage ist diese Arbeiten durchzuführen.&lt;/p&gt;

&lt;p&gt;Professioneller Support und Trainings stehen in den USA und in Europa zur Verfügung.&lt;/p&gt;

&lt;p&gt;Open Source ist die Basis für den Erfolg vieler Unternehmen. Deshalb: Unterstützt Eure Community.&lt;/p&gt;

&lt;p&gt;Für weitere Informationen stehen &lt;a href="//mailto:info@betadots.de"&gt;wir&lt;/a&gt; gerne zur Verfügung.&lt;/p&gt;

&lt;p&gt;Datacenters need automation&lt;/p&gt;

&lt;p&gt;betadots GmbH&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>opensource</category>
      <category>openvox</category>
      <category>cfgmgmt</category>
    </item>
    <item>
      <title>The Ruby side of Puppet - Part 3 - Custom Types and Providers</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Mon, 20 Jan 2025 18:50:29 +0000</pubDate>
      <link>https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma</link>
      <guid>https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma</guid>
      <description>&lt;p&gt;This is the final post in a three-part serie, covering the concepts and best practices for extending &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; using &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;custom facts&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_functions_ruby" rel="noopener noreferrer"&gt;custom functions&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;custom types&lt;/a&gt; and &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7"&gt;Part 1&lt;/a&gt; explores how to create custom facts, which allow nodes to send information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7"&gt;Part 2&lt;/a&gt; discusses building custom functions to process data or execute specific tasks.&lt;/p&gt;

&lt;p&gt;Part 3 (this post) focuses on custom types and providers, allowing you to extend Puppet's DSL and control system resources.&lt;/p&gt;




&lt;p&gt;Types are at the core of Puppet’s declarative DSL. Types describe the desired state of the system, targeting specific, configurable parts (sometimes OS-specific). Puppet provides a set of &lt;a href="https://www.puppet.com/docs/puppet/latest/type.html" rel="noopener noreferrer"&gt;core types&lt;/a&gt;, available with any Puppet agent installation.&lt;/p&gt;

&lt;p&gt;Some of the core types include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;li&gt;group&lt;/li&gt;
&lt;li&gt;package&lt;/li&gt;
&lt;li&gt;service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Create Custom Types and Providers&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;There are several reasons to develop custom types and providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid relying on the often unreliable or hard-to-maintain &lt;code&gt;exec&lt;/code&gt; resources&lt;/li&gt;
&lt;li&gt;Manage an application that requires CLI commands for configuration.&lt;/li&gt;
&lt;li&gt;Handle configurations with highly specific syntax that existing Puppet types cannot manage (like &lt;code&gt;file&lt;/code&gt; or &lt;code&gt;concat&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;General concepts&lt;/li&gt;
&lt;li&gt;Types and providers in modules&lt;/li&gt;
&lt;li&gt;Type description&lt;/li&gt;
&lt;li&gt;Provider implementation&lt;/li&gt;
&lt;li&gt;Using custom types in Puppet DSL&lt;/li&gt;
&lt;li&gt;Resource API&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  General concepts
&lt;/h2&gt;

&lt;p&gt;A custom type consists of two main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type definiton&lt;/strong&gt;: This defines how the type will be used in the Puppet DSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider implementation(s)&lt;/strong&gt;: One or more providers per type define how to interact with the system to manage resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Type definition
&lt;/h3&gt;

&lt;p&gt;The type describes how Puppet resources are declared in the DSL. For example, to manage an application’s configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;setting&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;   &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;property&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;param&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;app_args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;provider&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;auth_method&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the type definition, you typically have a &lt;strong&gt;namevar&lt;/strong&gt; (a key identifier) and mutiple &lt;strong&gt;parameters&lt;/strong&gt; and &lt;strong&gt;properties&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namevar&lt;/strong&gt;: The key identifying the resource instance in the type declaration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties&lt;/strong&gt;: These represent something measurable on the target system, such as a user’s UID or GID, or an application’s config value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameters&lt;/strong&gt;: Parameters influence how Puppet manages a resource but don’t directly map to something measurable on the system. For example, &lt;code&gt;manage_home&lt;/code&gt; in the &lt;code&gt;user&lt;/code&gt; type is a parameter that affects Puppet’s behavior but isn’t a property of the user account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between parameters and properties is nicely described on the &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#custom_types" rel="noopener noreferrer"&gt;Puppet Type/Provider development page&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Properties:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Properties correspond to something measurable on the target system. For example, the UID and GID of a user account are properties, because their current state can be queried or changed. In practical terms, setting a value for a property causes a method to be called on the provider."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Parameters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Parameters change how Puppet manages a resource, but do not necessarily map directly to something measurable. For example, the user type’s managehome attribute is a parameter — its value affects what Puppet does, but the question of whether Puppet is managing a home directory isn’t an innate property of the user account."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our example, the property is the value of the config setting, whereas the parameter specifies application attributes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider implementations
&lt;/h3&gt;

&lt;p&gt;Providers define the mechanisms for managing the state of the resources described by types. They handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Determining whether the resource already exists.&lt;/li&gt;
&lt;li&gt;Creating or removing resources.&lt;/li&gt;
&lt;li&gt;Modifying resources.&lt;/li&gt;
&lt;li&gt;Optionally, listing all existing resources of the type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Types and providers in modules
&lt;/h2&gt;

&lt;p&gt;Custom types and providers are placed within the &lt;code&gt;lib/puppet&lt;/code&gt; directory of a module.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The type file is located in &lt;code&gt;lib/puppet/type&lt;/code&gt; and named after the resource type (&lt;code&gt;app_config.rb&lt;/code&gt; in this case).&lt;/li&gt;
&lt;li&gt;Provider implementations go into the &lt;code&gt;lib/puppet/provider&lt;/code&gt; directory, inside a subdirectory named after the resource type. Each provider is named according to its provider implementation (e.g., &lt;code&gt;ruby.rb&lt;/code&gt;, &lt;code&gt;cli.rb&lt;/code&gt;, &lt;code&gt;cert.rb&lt;/code&gt;, &lt;code&gt;token.rb&lt;/code&gt;, &lt;code&gt;user.rb&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type: app_config&lt;/li&gt;
&lt;li&gt;Providers:

&lt;ul&gt;
&lt;li&gt;cert&lt;/li&gt;
&lt;li&gt;token&lt;/li&gt;
&lt;li&gt;user
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# &amp;lt;modulepath&amp;gt;/&amp;lt;modulename&amp;gt;
modules/application/
    \- lib/
        \- puppet/
            |- type/
            |    \- app_config.rb
            \- provider/
                \- app_config/
                    |- cert.rb
                    |- token.rb
                    \- user.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Type description
&lt;/h2&gt;

&lt;p&gt;New custom types can be crated using two different API versions.&lt;br&gt;
APIv1 is the old, classic way using getters and setters within the providers.&lt;br&gt;
APIv2 is a new implementation which is integrated into PDK - this implementation is also called &lt;code&gt;Resource API&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the next section we introduce the APIv1 implementation. The Resource API implementation is handled later in this document.&lt;/p&gt;
&lt;h3&gt;
  
  
  APIv1
&lt;/h3&gt;

&lt;p&gt;The traditional method uses &lt;code&gt;Puppet::Type.newtype&lt;/code&gt;, which defines the type.&lt;br&gt;
It is recommended to add the type documentation into the code.&lt;br&gt;
This allows users to run &lt;code&gt;puppet describe &amp;lt;type&amp;gt;&lt;/code&gt; on their system to display the documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="c1"&gt;# ... the code ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Management
&lt;/h3&gt;

&lt;p&gt;The most important feature of a type is to add or remove something from the system.&lt;br&gt;
This is usually handled with the &lt;code&gt;ensure&lt;/code&gt; property.&lt;br&gt;
To enable &lt;code&gt;ensure&lt;/code&gt;, a single line is needed: &lt;code&gt;ensurable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Namevar
&lt;/h3&gt;

&lt;p&gt;When declaring the type, one must specify a title - one could refer to this as a type instance identifier.&lt;br&gt;
Usually this reflects for something the type is managing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'betadots'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c"&gt;# &amp;lt;- title
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most simple implementation is to add a parameter with the name &lt;code&gt;:name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing the &lt;code&gt;namevar: true&lt;/code&gt; key to the parameter is another way to identify a namevar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;namevar: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s1"&gt;'The app config setting key to manage. see app_cli conf --help'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Properties
&lt;/h3&gt;

&lt;p&gt;Next we add the other property. Each property can be validated by its content.&lt;br&gt;
In our demo case we expect a string value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config value to set."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a valid value"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides this one can provide specific valid values which are validated automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:enable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;newvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;newvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that arrays as property values are validated in a different way:&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters" rel="noopener noreferrer"&gt;Puppet custom types&lt;/a&gt; website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, if a property is assigned multiple values in an array:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is considered in sync if any of those values matches the current value.&lt;/li&gt;
&lt;li&gt;If none of those values match, the first one is used when syncing the property.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;If all array values should be matched, the property needs the&lt;br&gt;
&lt;code&gt;array_matching&lt;/code&gt;to be set to &lt;code&gt;:all&lt;/code&gt;. The default value is &lt;code&gt;:first&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing values can be done in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt; for properties, &lt;code&gt;value&lt;/code&gt; for parameters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; for both, properties and parameters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We prefer to use the explizit methods as this makes it more clear, whether we deal with a property or a parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameters
&lt;/h3&gt;

&lt;p&gt;Parameters are defined in a similar way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; ['-p'], # persistency
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config value to set."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a valid value"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"CLI options to use during command execution."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a an array"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="n"&gt;defaultto&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-p'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Boolean parameters
&lt;/h3&gt;

&lt;p&gt;Parameter which get a bool value should be handled in a different way to avoid code repetition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/parameter/boolean'&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:force&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Parameter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatic relationships
&lt;/h3&gt;

&lt;p&gt;Within the type we can specify soft dependencies between different types.&lt;/p&gt;

&lt;p&gt;E.g. the app_cli should use a user which must be available on the system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;autorequire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on the new custom type can already be used in Puppet DSL, the compiler will create a catalog but the agent will produce an error, as there are no functional providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent side prerun evaluation
&lt;/h3&gt;

&lt;p&gt;It is possible to have the agent first check some things prior the catalog will be applied.&lt;br&gt;
This is what the &lt;code&gt;:pre_run_check&lt;/code&gt; method can be used for&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pre_run_check&lt;/span&gt;
  &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app.exe'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"App not installed"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;When using multiple providers (similar to the package resource) we want to be sure that the provider has all required implementations (features).&lt;br&gt;
A type can require a feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newtype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="vi"&gt;@doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; '/opt/app/etc/app.cfg',
        cli_args =&amp;gt; ['-p'], # persistency
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="c1"&gt;# global feature&lt;/span&gt;
  &lt;span class="c1"&gt;# feature :cli, "The cli feature requires some parameters", :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app config setting to manage. see app_cli conf --help"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config value to set."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/^\w+/&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a valid value"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# The property config_file must be set when using the cli &lt;/span&gt;
  &lt;span class="c1"&gt;#   option to configure app.&lt;/span&gt;
  &lt;span class="c1"&gt;# The cli provider will check for a feature.&lt;/span&gt;
  &lt;span class="n"&gt;newproperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:required_features&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sx"&gt;%w{cli}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The config file to use."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not an absolute path"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="ss"&gt;defaultto: &lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/etc/app.cfg'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;newparam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:array_matching&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"CLI options to use during command execution."&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ArgumentError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%s is not a an array"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="ss"&gt;defaultto: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'-p'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider implementation
&lt;/h2&gt;

&lt;p&gt;Once the type is defined, the provider must handle how the resource is managed. Providers typically implement methods to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if the resource exists (&lt;code&gt;exists?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Create a new resource (&lt;code&gt;create&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Read existing resource properties (&lt;code&gt;prefetch&lt;/code&gt; or a getter)&lt;/li&gt;
&lt;li&gt;Modify a resource (&lt;code&gt;flush&lt;/code&gt; or a setter).&lt;/li&gt;
&lt;li&gt;Delete a resource (&lt;code&gt;destroy&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also possible to re-use and extend existing providers classes.&lt;br&gt;
Common code can be placed inside a generic provider (&lt;code&gt;lib/puppet/provider/app_config.rb&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;App_config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider reuses shared/common code from app_config.rb."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to add shared functionality to multiple providers, you can place your code into the PuppetX module directory: &lt;code&gt;lib/puppet_x/&amp;lt;company_name&amp;gt;/&amp;lt;unique class name&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../../puppet_x/betadots/app_api.rb'&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider reuses shared/common code from app_config.rb."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new provider can be created by inheriting from and extending an existing provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:source&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types token provideris an extension to the api provider."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally one can reuse any provider from any type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:parent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ini_setting&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ruby&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types token provideris an extension to the api provider."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selection of provider
&lt;/h3&gt;

&lt;p&gt;The Puppet Agent must know which provider to use, if multiple providers are present.&lt;br&gt;
You can use of the &lt;code&gt;provider&lt;/code&gt; meta parameterr or let providers perform checks to determine if they are valid for the system. The options include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;commands&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commands :app =&amp;gt; "/opt/app/bin/app.exe"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the command &lt;code&gt;/opt/app/bin/app.exe&lt;/code&gt; exists. The command can be later used using &lt;code&gt;:app&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - exists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :exists =&amp;gt; "/opt/app/etc/app.conf"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the file exists.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :true =&amp;gt; /^10\./.match(%x{/opt/app/bin/app.exec version})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if Version is 10.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - Fact&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the fact value matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confing - feature&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :feature =&amp;gt; :cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the provider has the feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaultfor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaultfor 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use this provider as default for Debian-based systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Reading and applying configuration
&lt;/h3&gt;

&lt;p&gt;The provider needs the ability to create, read, update and delete individual configuration states (CRUD API).&lt;/p&gt;

&lt;p&gt;Each configuration property needs a &lt;code&gt;getter&lt;/code&gt; (read) and a &lt;code&gt;setter&lt;/code&gt; (modify) method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="c1"&gt;#   app_config: { 'enable_logging':&lt;/span&gt;
&lt;span class="c1"&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class="c1"&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class="c1"&gt;#   }&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:app_config&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:cli&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"The app_config types cli provider."&lt;/span&gt;

  &lt;span class="c1"&gt;# confine to existing command&lt;/span&gt;
  &lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="ss"&gt;:app&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"/opt/app/bin/app.exe"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exists?&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{^&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ParserError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'found multiple config items found, please fix this'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'set'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'rm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# getter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# setter&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'set'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is recommended to also create the &lt;code&gt;instances&lt;/code&gt; class method, which can collect all instances of a resource type into a hash.&lt;br&gt;
The namevar is the hash key, which has a hash of paramters and properties as value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'yaml'&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instances&lt;/span&gt;
  &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_cli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'list'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;attributes_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;key: &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ensure: :present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;instances&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes it is not possible to collect all instances, such as with the &lt;code&gt;file&lt;/code&gt; resource. In such cases no &lt;code&gt;instance&lt;/code&gt; method is defined.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;instance&lt;/code&gt; method is used indirectly be each type when calling &lt;code&gt;prefetch&lt;/code&gt; to get the acutal configuration and returns the&lt;code&gt;@property_hash&lt;/code&gt; instance variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_key&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;instances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the type getter and setter to read an manipulate the instance variable, instead of writing &lt;code&gt;getter&lt;/code&gt; and &lt;code&gt;setter&lt;/code&gt; methods for each type.&lt;br&gt;
This behavior is added by declaring the &lt;code&gt;mk_resource_methods&lt;/code&gt; class method.&lt;/p&gt;

&lt;p&gt;Once this is implemented you can run the command &lt;code&gt;puppet resource app_config&lt;/code&gt; to retrieve all existing configurations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Refresh events
&lt;/h3&gt;

&lt;p&gt;In some cases it is required to &lt;code&gt;refresh&lt;/code&gt; a resource, for example, to restart a service, remount a mount, or rerun an exec resource. To allow a type/provider to react to a refresh event, it needs special handling.&lt;/p&gt;

&lt;p&gt;Within the type the &lt;code&gt;refreshable&lt;/code&gt; feature must be activated and a &lt;code&gt;refresh&lt;/code&gt; definition is added.&lt;/p&gt;

&lt;p&gt;The following examples are taken from puppet &lt;code&gt;service&lt;/code&gt; type and provider. The feature describes, which provider definition should be executed upon refresh event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="ss"&gt;:refreshable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"The provider can restart the service."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:methods&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:restart&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refresh&lt;/span&gt;
    &lt;span class="c1"&gt;# Only restart if we're actually running&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:ensure&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;newattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ensure&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:running&lt;/span&gt;
      &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;restart&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="s2"&gt;"Skipping restart; service is not running"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using custom types in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Once the custom type and provider are implemented, you can use them in your manifests as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# Type declaration
&lt;/span&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'enable_logging'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;value&lt;/span&gt;     &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kp"&gt;require&lt;/span&gt;   &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/etc/app.cfg'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="kp"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;|,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Type reference
&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kp"&gt;subscribe&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;App_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'enable_logging'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Type declaration using lambda and splat operator
&lt;/span&gt;&lt;span class="nv"&gt;$config_hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Hash&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Virtual or exported resource
&lt;/span&gt;&lt;span class="err"&gt;@@&lt;/span&gt;&lt;span class="n"&gt;app_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="s1"&gt;'app_mount'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;value&lt;/span&gt;  &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;{facts['networking']['fqdn']}/srv/app_mount"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="py"&gt;tag&lt;/span&gt;    &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'blog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Resource collector
&lt;/span&gt;&lt;span class="nc"&gt;App_config&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;|&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'blog'&lt;/span&gt; &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resource API
&lt;/h2&gt;

&lt;p&gt;The modern resource API has one limitation: one can not refresh types!&lt;br&gt;
Besides this it also consists of types and providers which must be placed at the same location as the existing implementation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Resource API Type
&lt;/h3&gt;

&lt;p&gt;The resource API type uses an attributes hash to list all parameters and properties. Within the &lt;code&gt;type&lt;/code&gt; key one can use any of the built-in Puppet data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/resource_api'&lt;/span&gt;

&lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResourceApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s1"&gt;'app_config'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;docs: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;
      @summary The type to manage app config settings
      @example
      app_config { 'key':
        ensure =&amp;gt; present,
        value  =&amp;gt; 'value',
      }

      This type provides Puppet with the capabilities to manage our application config
&lt;/span&gt;&lt;span class="no"&gt;    EOS&lt;/span&gt;
  &lt;span class="ss"&gt;features: &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="ss"&gt;attributes: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;ensure: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type:    &lt;/span&gt;&lt;span class="s1"&gt;'Enum[present, absent]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc:    &lt;/span&gt;&lt;span class="s1"&gt;'Whether the setting should be added or removed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type:     &lt;/span&gt;&lt;span class="s1"&gt;'String'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc:     &lt;/span&gt;&lt;span class="s1"&gt;'The setting to manage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;behavior: :namevar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="ss"&gt;value: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'Variant[String, Integer, Array]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;desc: &lt;/span&gt;&lt;span class="s1"&gt;'The value to set'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Respource API Provider
&lt;/h3&gt;

&lt;p&gt;The most simple solution is to make use of the &lt;code&gt;SimpleProvider&lt;/code&gt; class.&lt;br&gt;
This provider needs up to 4 defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get&lt;/li&gt;
&lt;li&gt;create&lt;/li&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;delete
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'puppet/resource_api/simple_provider'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'open3'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Puppet::Provider::AppConfig::Cli&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Puppet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResourceApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SimpleProvider&lt;/span&gt;
  &lt;span class="vg"&gt;$app_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/opt/app/bin/app.exe'&lt;/span&gt;

  &lt;span class="c1"&gt;# Get: fetching all readable config settings into a hash&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Returning data from command'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/opt/app/bin/app.exe list'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;ParseError&lt;/span&gt; &lt;span class="s2"&gt;"Error running command: &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="vi"&gt;@command_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;%r{&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@command_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'---'&lt;/span&gt;
      &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;
      &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;'ensure'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'present'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'value'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="vi"&gt;@result&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Create: add a new config setting with name and should value&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Creating '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' with &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Update: correct an existing setting with name and replace with should value&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Updating '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' with &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;should&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Delete: remove a config setting&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;notice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Deleting '&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Open3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;capture3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Types and providers are not complex. They basically describe the CRUD behavior of Puppet.&lt;br&gt;
The type defines how to use the Puppet DSL, while the provider controls how to check and handle specific settings.&lt;/p&gt;

&lt;p&gt;Please stop using &lt;code&gt;exec&lt;/code&gt; for almost anything that is not super simple. In most cases, a simple type and provider will allow better analysis, error handling and control over what is happening. Besides this: &lt;strong&gt;custom types and providers execute faster&lt;/strong&gt; compared to &lt;code&gt;exec&lt;/code&gt; type usage - far more faster!&lt;/p&gt;

&lt;p&gt;The example application and the working types and providers are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app: &lt;a href="https://github.com/betadots/workshop-demo-app" rel="noopener noreferrer"&gt;workshop demo app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Puppet module with types and providers for the app: &lt;a href="https://github.com/betadots/workshop-demo-module/tree/ruby_workshop" rel="noopener noreferrer"&gt;workshop demo module&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install APP:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-app /opt/app
/opt/app/bin/app.exe

&lt;span class="c"&gt;# Install Puppet module:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class="nt"&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class="c"&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class="nt"&gt;--modulepath&lt;/span&gt; modules
&lt;span class="c"&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class="nt"&gt;--modulepath&lt;/span&gt; modules

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;

</description>
      <category>puppet</category>
      <category>ruby</category>
    </item>
  </channel>
</rss>
