<?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>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>
    <item>
      <title>Die Ruby-Seite von Puppet - Teil 3 - Benutzerdefinierte Typen und Provider</title>
      <dc:creator>Martin Alfke</dc:creator>
      <pubDate>Mon, 20 Jan 2025 18:48:32 +0000</pubDate>
      <link>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp</link>
      <guid>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp</guid>
      <description>&lt;p&gt;Dies ist der letzte Beitrag in einer dreiteiligen Serie, die die Konzepte und Best Practices für die Erweiterung von &lt;a href="https://www.puppet.com" rel="noopener noreferrer"&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_facts" rel="noopener noreferrer"&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_functions_ruby" rel="noopener noreferrer"&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types" rel="noopener noreferrer"&gt;benutzerdefinierten Typen&lt;/a&gt; sowie &lt;a href="https://www.puppet.com/docs/puppet/latest/provider_development" rel="noopener noreferrer"&gt;Providern&lt;/a&gt; behandelt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj"&gt;Teil 1&lt;/a&gt; untersucht, wie man benutzerdefinierte Fakten erstellt, die es Knoten ermöglichen, Informationen an den Puppet-Server zu senden.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3"&gt;Teil 2&lt;/a&gt; behandelt den Aufbau benutzerdefinierter Funktionen zur Verarbeitung von Daten oder zur Ausführung spezifischer Aufgaben.&lt;/p&gt;

&lt;p&gt;Teil 3 (dieser Beitrag) konzentriert sich auf benutzerdefinierte Typen und Provider, mit denen Puppets DSL erweitert und Systemressourcen verwaltet werden können.&lt;/p&gt;




&lt;p&gt;Typen stehen im Zentrum der deklarativen DSL von Puppet. Typen beschreiben den gewünschten Zustand des Systems und zielen auf spezifische, konfigurierbare Teile ab (manchmal betriebssystemspezifisch). Puppet bietet eine Reihe von &lt;a href="https://www.puppet.com/docs/puppet/latest/type.html" rel="noopener noreferrer"&gt;Kern-Typen&lt;/a&gt;, die mit jeder Puppet-Agent-Installation verfügbar sind.&lt;/p&gt;

&lt;p&gt;Einige der Kern-Typen umfassen:&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;Warum benutzerdefinierte Typen und Provider erstellen&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Es gibt mehrere Gründe, benutzerdefinierte Typen und Provider zu entwickeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vermeiden von den oft unzuverlässigen oder schwer zu wartenden &lt;code&gt;exec&lt;/code&gt;-Ressourcen.&lt;/li&gt;
&lt;li&gt;Verwalten einer Anwendung, die CLI-Befehle zur Konfiguration benötigt.&lt;/li&gt;
&lt;li&gt;Handhaben von Konfigurationen mit hochspezifischer Syntax, die bestehende Puppet-Typen nicht verwalten können (wie &lt;code&gt;file&lt;/code&gt; oder &lt;code&gt;concat&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
&lt;li&gt;Allgemeine Konzepte&lt;/li&gt;
&lt;li&gt;Typen und Provider in Modulen&lt;/li&gt;
&lt;li&gt;Typbeschreibung&lt;/li&gt;
&lt;li&gt;Provider-Implementierung&lt;/li&gt;
&lt;li&gt;Verwendung benutzerdefinierter Typen in der Puppet DSL&lt;/li&gt;
&lt;li&gt;Resourcen-API&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Allgemeine Konzepte
&lt;/h2&gt;

&lt;p&gt;Ein Typ besteht aus zwei Hauptteilen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typdefinition&lt;/strong&gt;: Diese definiert, wie der Typ in der Puppet DSL verwendet wird.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider-Implementierung(en)&lt;/strong&gt;: Ein oder mehrere Provider pro Typ definieren, wie mit dem System interagiert wird, um Ressourcen zu verwalten.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Typdefinition
&lt;/h3&gt;

&lt;p&gt;Der Typ beschreibt, wie Puppet-Ressourcen in der DSL deklariert werden. Zum Beispiel, um die Konfiguration einer Anwendung zu verwalten:&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;Typdefinition haben einen &lt;strong&gt;Namevar&lt;/strong&gt; (ein Schlüsselbezeichner) und mehrere &lt;strong&gt;Parameter&lt;/strong&gt; und &lt;strong&gt;Properties (Eigenschaften)&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namevar&lt;/strong&gt;: Der Schlüssel, der die Ressourceninstanz in der Typdeklaration identifiziert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties (Eigenschaften)&lt;/strong&gt;: Diese repräsentieren etwas Messbares im Zielsystem, wie die UID oder GID eines Benutzers oder einen Konfigurationswert einer Anwendung.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter&lt;/strong&gt;: Parameter beeinflussen, wie Puppet eine Ressource verwaltet, spiegeln jedoch nicht direkt etwas Messbares im System wider. Zum Beispiel ist &lt;code&gt;manage_home&lt;/code&gt; im &lt;code&gt;user&lt;/code&gt;-Typ ein Parameter, der Puppets Verhalten beeinflusst, aber keine Eigenschaft des Benutzerkontos ist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Der Unterschied zwischen Parametern und Eigenschaften wird auf der &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#custom_types" rel="noopener noreferrer"&gt;Puppet-Typ/Provider-Entwicklungsseite&lt;/a&gt; gut beschrieben:&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;"Eigenschaften entsprechen etwas Messbarem im Zielsystem. Zum Beispiel sind die UID und GID eines Benutzerkontos Eigenschaften, da ihr aktueller Zustand abgefragt oder geändert werden kann. Praktisch bedeutet das, dass das Festlegen eines Wertes für eine Eigenschaft eine Methode im Provider aufruft."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Parameter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Parameter ändern, wie Puppet eine Ressource verwaltet, entsprechen jedoch nicht unbedingt direkt etwas Messbarem. Zum Beispiel ist das managehome-Attribut des Benutzertyps ein Parameter – sein Wert beeinflusst, was Puppet tut, aber die Frage, ob Puppet ein Home-Verzeichnis verwaltet, ist keine angeborene Eigenschaft des Benutzerkontos."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In unserem Beispiel ist die Eigenschaft der Wert des Konfigurationseinstellungen, während der Parameter die Anwendungsattribute angibt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider-Implementierungen
&lt;/h3&gt;

&lt;p&gt;Provider definieren die Mechanismen zur Verwaltung des Zustands der durch Typen beschriebenen Ressourcen. Sie behandeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bestimmung, ob die Ressource bereits existiert&lt;/li&gt;
&lt;li&gt;Erstellen oder Entfernen von Ressourcen.&lt;/li&gt;
&lt;li&gt;Ändern von Ressourcen.&lt;/li&gt;
&lt;li&gt;Optional das Auflisten aller vorhandenen Ressourcen des Typs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Typen und Provider in Modulen
&lt;/h2&gt;

&lt;p&gt;Benutzerdefinierte Typen und Provider werden im Verzeichnis &lt;code&gt;lib/puppet&lt;/code&gt; eines Moduls platziert.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Die Typdatei befindet sich in &lt;code&gt;lib/puppet/type&lt;/code&gt; und ist nach dem Ressourcentyp benannt (&lt;code&gt;app_config.rb&lt;/code&gt; in diesem Fall).&lt;/li&gt;
&lt;li&gt;Provider-Implementierungen gehen in das Verzeichnis &lt;code&gt;lib/puppet/provider&lt;/code&gt;, in ein Unterverzeichnis, das nach dem Ressourcentyp benannt ist. Jeder Provider ist nach seiner Provider-Implementierung benannt (z.B. &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;Beispiel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Typ: app_config&lt;/li&gt;
&lt;li&gt;Provider:

&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;modulpfad&amp;gt;/&amp;lt;modulname&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;
  
  
  Typbeschreibung
&lt;/h2&gt;

&lt;p&gt;Neue benutzerdefinierte Typen können mit zwei verschiedenen API-Versionen erstellt werden. APIv1 ist die alte, klassische Methode, die Getter und Setter innerhalb der Provider verwendet. APIv2 ist eine neue Implementierung, die in PDK integriert ist – diese Implementierung wird auch als &lt;code&gt;Resource-API&lt;/code&gt; bezeichnet.&lt;/p&gt;

&lt;p&gt;Im nächsten Abschnitt stellen wir die APIv1-Implementierung vor. Die Implementierung der Ressourcen-API wird später in diesem Dokument behandelt.&lt;/p&gt;

&lt;h3&gt;
  
  
  APIv1
&lt;/h3&gt;

&lt;p&gt;Die traditionelle Methode verwendet &lt;code&gt;Puppet::Type.newtype&lt;/code&gt;, die den Typ definiert. Es wird empfohlen, die Typdokumentation in den Code einzufügen. Dies ermöglicht es den Benutzern, &lt;code&gt;puppet describe &amp;lt;type&amp;gt;&lt;/code&gt; auf ihrem System auszuführen, um die Dokumentation anzuzeigen.&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{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class="c1"&gt;# ... der 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;
  
  
  Verwaltung
&lt;/h3&gt;

&lt;p&gt;Die wichtigste Funktion eines Typs besteht darin, etwas zum System hinzuzufügen oder zu entfernen. Dies wird normalerweise mit der &lt;code&gt;ensure&lt;/code&gt;-Eigenschaft behandelt. Um &lt;code&gt;ensure&lt;/code&gt; zu aktivieren, ist eine einzige Zeile erforderlich: &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{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      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;Bei der Deklaration des Typs muss ein Titel angegeben werden – dies könnte man als Typinstanz-Identifikator bezeichnen. In der Regel spiegelt dies wider, wofür der Typ verantwortlich ist.&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;- Titel
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die einfachste Implementierung besteht darin, einen Parameter mit dem Namen &lt;code&gt;:name&lt;/code&gt; hinzuzufügen.&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{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      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;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe 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;Das Übergeben des Schlüssels &lt;code&gt;namevar: true&lt;/code&gt; an den Parameter ist eine weitere Möglichkeit, einen Namevar zu identifizieren:&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;'Der Schlüssel des Anwendungsconfig-Elements, das verwaltet werden soll. Siehe 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;
  
  
  Eigenschaften
&lt;/h3&gt;

&lt;p&gt;Als Nächstes fügen wir die andere Eigenschaft hinzu. Jede Eigenschaft kann durch ihren Inhalt validiert werden. In unserem Demo-Fall erwarten wir einen Stringwert.&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{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      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;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe 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;"Der zu setzende Konfigurationswert."&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 ist kein gültiger Wert"&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;Außerdem kann man spezifische gültige Werte angeben, die automatisch validiert werden:&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;Bitte beachten, dass Arrays als Eigenschaftswerte auf andere Weise validiert werden:&lt;/p&gt;

&lt;p&gt;Von der Website &lt;a href="https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters" rel="noopener noreferrer"&gt;Puppet benutzerdefinierte Typen&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Standardmäßig wird eine Eigenschaft, die mehrere Werte in einem Array zugewiesen bekommt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Als synchron betrachtet, wenn einer dieser Werte dem aktuellen Wert entspricht.&lt;/li&gt;
&lt;li&gt;Wenn keiner dieser Werte übereinstimmt, wird der erste Wert beim Synchronisieren der Eigenschaft verwendet.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wenn alle Array-Werte übereinstimmen sollen, muss die Eigenschaft &lt;code&gt;array_matching&lt;/code&gt; auf &lt;code&gt;:all&lt;/code&gt; gesetzt werden. Der Standardwert ist &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;Der Zugriff auf Werte kann auf zwei Arten erfolgen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt; für Eigenschaften, &lt;code&gt;value&lt;/code&gt; für Parameter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; für sowohl Eigenschaften als auch Parameter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wir bevorzugen die expliziten Methoden, da dies klarer macht, ob wir es mit einer Eigenschaft oder einem Parameter zu tun haben.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameter
&lt;/h3&gt;

&lt;p&gt;Parameter werden auf ähnliche Weise definiert:&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{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { 'enable_logging':
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; ['-p'], # Persistenz
      }
  }&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;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe 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;"Der zu setzende Konfigurationswert."&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 ist kein gültiger Wert"&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-Optionen, die während der Befehlsausführung verwendet werden sollen."&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 ist kein 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-Parameter
&lt;/h3&gt;

&lt;p&gt;Parameter, die einen booleschen Wert erhalten, sollten auf andere Weise behandelt werden, um Codewiederholungen zu vermeiden.&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;
  
  
  Automatische Abhängigkeiten
&lt;/h3&gt;

&lt;p&gt;Innerhalb des Typs können wir weiche Abhängigkeiten zwischen verschiedenen Typen angeben.&lt;/p&gt;

&lt;p&gt;Beispiel: Das &lt;code&gt;app_cli&lt;/code&gt; sollte einen Benutzer verwenden, der im System verfügbar sein muss.&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;Von nun an kann der neue benutzerdefinierte Typ bereits in Puppet DSL verwendet werden, der Compiler wird ein Katalog erstellen, aber der Agent wird einen Fehler produzieren, da es keine funktionalen Provider gibt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vorabprüfung auf der Agentenseite
&lt;/h3&gt;

&lt;p&gt;Es ist möglich, dass der Agent zunächst einige Dinge überprüft, bevor der Katalog angewendet wird. Dafür kann die Methode &lt;code&gt;:pre_run_check&lt;/code&gt; verwendet werden.&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 nicht installiert"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Merkmale
&lt;/h3&gt;

&lt;p&gt;Bei der Verwendung mehrerer Provider (ähnlich der Ressourcenpakete) wollen wir sicherstellen, dass der Provider alle erforderlichen Implementierungen (Merkmale - Features) hat. Ein Typ kann ein Merkmal erfordern:&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{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      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'], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class="n"&gt;ensurable&lt;/span&gt;
  &lt;span class="c1"&gt;# globales Merkmal&lt;/span&gt;
  &lt;span class="c1"&gt;# feature :cli, "Das CLI-Merkmal erfordert einige Parameter", :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;"Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe 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;"Der zu setzende Konfigurationswert."&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 ist kein gültiger Wert"&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;# Die Eigenschaft config_file muss gesetzt werden, wenn die CLI&lt;/span&gt;
  &lt;span class="c1"&gt;#   Option zur Konfiguration von App verwendet wird.&lt;/span&gt;
  &lt;span class="c1"&gt;# Der CLI-Provider wird nach einem Merkmal suchen.&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;"Die zu verwendende Konfigurationsdatei."&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 ist kein absoluter Pfad"&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="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-Optionen, die während der Befehlsausführung verwendet werden sollen."&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 ist kein 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;h2&gt;
  
  
  Provider-Implementierung
&lt;/h2&gt;

&lt;p&gt;Sobald der Typ definiert ist, muss der Anbieter steuern, wie die Ressource verwaltet wird. Anbieter implementieren typischerweise Methoden, um:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Überprüfen, ob die Ressource existiert (&lt;code&gt;exists?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Eine neue Ressource erstellen (&lt;code&gt;create&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Vorhandene Ressourcenattribute lesen (&lt;code&gt;prefetch&lt;/code&gt; oder ein Getter).&lt;/li&gt;
&lt;li&gt;Eine Ressource ändern (&lt;code&gt;flush&lt;/code&gt; oder ein Setter).&lt;/li&gt;
&lt;li&gt;Eine Ressource löschen (&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;"Der CLI-Anbieter des app_config-Typs."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es ist auch möglich, bestehende Anbieterklassen wiederzuverwenden und zu erweitern. Gemeinsamer Code kann in einem generischen Anbieter (&lt;code&gt;lib/puppet/provider/app_config.rb&lt;/code&gt;) platziert werden.&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;"Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus 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;Wenn eine gemeinsame Funktionalität für mehrere Anbieter hinzugefügt werden soll, kann man den Code im Puppet_X-Modulverzeichnis ablegen: &lt;code&gt;lib/puppet_x/&amp;lt;unternehmens_name&amp;gt;/&amp;lt;eindeutiger Klassenname&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;"Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus 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;Ein neuer Anbieter kann erstellt werden, indem er von einem bestehenden Anbieter erbt und diesen erweitert:&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;"Der Token-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zusätzlich kann man jeden Anbieter von jedem Typ wiederverwenden:&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;"Der Datei-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters."&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auswahl des Anbieters
&lt;/h3&gt;

&lt;p&gt;Der Puppet-Agent muss wissen, welcher Anbieter zu verwenden ist, wenn mehrere Anbieter vorhanden sind. Man kann den &lt;code&gt;provider&lt;/code&gt;-Meta-Parameter verwenden oder die Anbieter Überprüfungen durchführen lassen, um festzustellen, ob sie für das System gültig sind. Die Optionen umfassen:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Überprüfung&lt;/th&gt;
&lt;th&gt;Beispiel&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Befehle&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;Gültig, wenn der Befehl &lt;code&gt;/opt/app/bin/app.exe&lt;/code&gt; existiert. Der Befehl kann später mit &lt;code&gt;:app&lt;/code&gt; verwendet werden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - existiert&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;Gültig, wenn die Datei existiert.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - 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;Gültig, wenn die Version 10.x ist.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - Fakt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine 'os.family' =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Faktwert übereinstimmt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - Funktion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :feature =&amp;gt; :cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Anbieter die Funktion hat.&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;Anbieter als Standard für Debian-basierte Systeme.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lesen und Anwenden von Konfigurationen
&lt;/h3&gt;

&lt;p&gt;Der Anbieter benötigt die Fähigkeit, einzelne Konfigurationszustände zu erstellen, zu lesen, zu aktualisieren und zu löschen (CRUD-API).&lt;/p&gt;

&lt;p&gt;Jede Konfigurationseigenschaft benötigt eine &lt;code&gt;getter&lt;/code&gt;- (lesen) und eine &lt;code&gt;setter&lt;/code&gt;- (ändern) Methode.&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;"Der CLI-Anbieter des app_config-Typs."&lt;/span&gt;

  &lt;span class="c1"&gt;# Einschränkung auf bestehenden Befehl&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;'Mehrere Konfigurationselemente gefunden, bitte beheben Sie dies'&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;Es wird empfohlen, auch die &lt;code&gt;instances&lt;/code&gt;-Klassenmethode zu erstellen, die alle Instanzen eines Ressourcentyps in einem Hash sammeln kann. Der Namevar ist der Hash-Schlüssel, der einen Hash von Parametern und Eigenschaften als Wert hat.&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;Manchmal ist es nicht möglich, alle Instanzen zu sammeln, z. B. beim &lt;code&gt;file&lt;/code&gt;-Ressourcentyp. In solchen Fällen wird keine &lt;code&gt;instance&lt;/code&gt;-Methode definiert.&lt;/p&gt;

&lt;p&gt;Die &lt;code&gt;instance&lt;/code&gt;-Methode wird indirekt von jedem Typ verwendet, wenn &lt;code&gt;prefetch&lt;/code&gt; aufgerufen wird, um die aktuelle Konfiguration zu erhalten und die &lt;code&gt;@property_hash&lt;/code&gt;-Instanzvariable zurückzugeben.&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;Dies ermöglicht es dem Typ, Getter und Setter zu verwenden, um die Instanzvariable zu lesen und zu manipulieren, anstatt für jeden Typ &lt;code&gt;getter&lt;/code&gt;- und &lt;code&gt;setter&lt;/code&gt;-Methoden zu schreiben. Dieses Verhalten wird durch die Deklaration der &lt;code&gt;mk_resource_methods&lt;/code&gt;-Klassenmethode hinzugefügt.&lt;/p&gt;

&lt;p&gt;Sobald dies implementiert ist, kann man den Befehl &lt;code&gt;puppet resource app_config&lt;/code&gt; ausführen, um alle vorhandenen Konfigurationen abzurufen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refresh-Ereignisse
&lt;/h3&gt;

&lt;p&gt;In einigen Fällen ist es erforderlich, eine Ressource zu &lt;code&gt;aktualisieren&lt;/code&gt; (refresh), beispielsweise um einen Dienst neu zu starten, ein Laufwerk erneut zu mounten oder eine Exec-Ressource erneut auszuführen. Damit ein Typ/Anbieter auf ein Aktualisierungsereignis reagieren kann, ist eine spezielle Behandlung erforderlich.&lt;/p&gt;

&lt;p&gt;Innerhalb des Typs muss die &lt;code&gt;refreshable&lt;/code&gt;-Funktion aktiviert werden, und eine &lt;code&gt;refresh&lt;/code&gt;-Definition wird hinzugefügt.&lt;/p&gt;

&lt;p&gt;Die folgenden Beispiele stammen vom Puppet &lt;code&gt;service&lt;/code&gt; Typ und Anbieter. Die Funktion beschreibt, welche Anbieterdefinition bei einem Aktualisierungsereignis ausgeführt werden soll.&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;"Der Anbieter kann den Dienst neu starten."&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;# Nur neu starten, wenn wir tatsächlich laufen&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;"Neustart überspringen; Dienst läuft nicht"&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;
  
  
  Verwendung benutzerdefinierter Typen in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Sobald der benutzerdefinierte Typ und Anbieter implementiert sind, kann man diesen in Puppet Manifesten wie folgt verwenden:&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;# Typdeklaration
&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;# Typreferenz
&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;# Typdeklaration mit Lambda und 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="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="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;# Virtuelle oder exportierte Ressource
&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="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;# Ressourcen-Sammler
&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;
  
  
  Resourcen-API
&lt;/h2&gt;

&lt;p&gt;Die moderne Ressourcen-API hat eine Einschränkung: Man kann Typen nicht aktualisieren! Außerdem besteht sie aus Typen und Anbietern, die am selben Ort wie die bestehende Implementierung platziert werden müssen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ressourcen-API-Typ
&lt;/h3&gt;

&lt;p&gt;Der Ressourcen-API-Typ verwendet einen Attributs-Hash, um alle Parameter und Eigenschaften aufzulisten. Innerhalb des &lt;code&gt;type&lt;/code&gt;-Schlüssels kann man jeden der integrierten Puppet-Datentypen verwenden.&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 Der Typ zur Verwaltung von App-Konfigurationseinstellungen
      @example
      app_config { 'key':
        ensure =&amp;gt; present,
        value  =&amp;gt; 'value',
      }

      Dieser Typ bietet Puppet die Möglichkeit, unsere Anwendungsconfig zu verwalten.
&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;'Ob die Einstellung hinzugefügt oder entfernt werden soll'&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;'Die Einstellung, die verwaltet werden soll'&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;'Der Wert, der gesetzt werden soll'&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;
  
  
  Ressourcen-API-Anbieter
&lt;/h3&gt;

&lt;p&gt;Die einfachste Lösung ist die Verwendung der Klasse &lt;code&gt;SimpleProvider&lt;/code&gt;. Dieser Anbieter benötigt bis zu 4 Definitionen:&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: Alle lesbaren Konfigurationseinstellungen in einen Hash abrufen&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;'Gibt Daten vom Befehl zurück'&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;"Fehler beim Ausführen des Befehls: &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;# Fehlende Fehlerbehandlung. Dies dient nur zur 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: Eine neue Konfigurationseinstellung mit Name und Sollwert hinzufügen&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;"Erstelle '&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;' mit &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;# Fehlende Fehlerbehandlung. Dies dient nur zur 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: Eine vorhandene Einstellung mit Namen korrigieren und durch den Sollwert ersetzen&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;"Aktualisiere '&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;' mit &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;# Fehlende Fehlerbehandlung. Dies dient nur zur 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: Eine Konfigurationseinstellung entfernen&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;"Lösche '&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;# Fehlende Fehlerbehandlung. Dies dient nur zur 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;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Typen und Anbieter sind nicht komplex. Sie beschreiben im Grunde das CRUD-Verhalten von Puppet. Der Typ definiert, wie man die Puppet DSL verwendet, während der Anbieter steuert, wie spezifische Einstellungen überprüft und behandelt werden.&lt;/p&gt;

&lt;p&gt;Eine exzessive Nutzung der &lt;code&gt;exec&lt;/code&gt; Resource ist zu vermweiden. In den meisten Fällen ermöglicht ein einfacher Typ und Anbieter eine bessere Analyse, Fehlerbehandlung und Kontrolle über das, was passiert. Darüber hinaus: &lt;strong&gt;benutzerdefinierte Typen und Anbieter führen schneller aus&lt;/strong&gt; im Vergleich zur Verwendung des &lt;code&gt;exec&lt;/code&gt;-Typs - viel schneller!&lt;/p&gt;

&lt;p&gt;Die Beispielanwendung und die funktionierenden Typen und Anbieter aus diesem Posting sind auf GitHub verfügbar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Die 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;Das Puppet-Modul mit Typen und Anbietern für die App: &lt;a href="https://github.com/betadots/workshop-demo-module/tree/ruby_workshop" rel="noopener noreferrer"&gt;Workshop-Demo-Modul&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App-Nutzung:&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;# APP installieren:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-app /opt/app
/opt/app/bin/app.exe

&lt;span class="c"&gt;# Puppet-Modul installieren:&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;Viel Spaß beim Puppetisieren,&lt;br&gt;&lt;br&gt;
Martin&lt;/p&gt;

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